mirror of
https://github.com/cryptomator/cryptomator.git
synced 2024-11-23 03:59:51 +00:00
Merge branch 'develop' into feature/3203-device-already-registered
This commit is contained in:
commit
a6d6474294
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -26,6 +26,7 @@ body:
|
||||
Examples:
|
||||
- Operating System: Windows 10
|
||||
- Cryptomator: 1.5.16
|
||||
- OneDrive: 23.226
|
||||
- LibreOffice: 7.1.4
|
||||
value: |
|
||||
- Operating System:
|
||||
|
31
.github/dependabot.yml
vendored
31
.github/dependabot.yml
vendored
@ -6,11 +6,38 @@ updates:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "06:00"
|
||||
timezone: "UTC"
|
||||
timezone: "Etc/UTC"
|
||||
groups:
|
||||
maven-dependencies:
|
||||
java-test-dependencies:
|
||||
patterns:
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
maven-build-plugins:
|
||||
patterns:
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
javafx:
|
||||
patterns:
|
||||
- "org.openjfx:*"
|
||||
java-production-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
exclude-patterns:
|
||||
- "org.openjfx:*"
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # even for `.github/workflows`
|
||||
|
4
.github/workflows/appimage.yml
vendored
4
.github/workflows/appimage.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
mvn -B verify
|
||||
jacoco:report
|
||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
-Pcoverage,dependency-check
|
||||
-Pcoverage
|
||||
-Dsonar.projectKey=cryptomator_cryptomator
|
||||
-Dsonar.organization=cryptomator
|
||||
-Dsonar.host.url=https://sonarcloud.io
|
||||
|
4
.github/workflows/check-jdk-updates.yml
vendored
4
.github/workflows/check-jdk-updates.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
outputs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ env.JDK_VERSION }}
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
|
4
.github/workflows/debian.yml
vendored
4
.github/workflows/debian.yml
vendored
@ -46,14 +46,14 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
|
56
.github/workflows/dependency-check.yml
vendored
Normal file
56
.github/workflows/dependency-check.yml
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
name: OWASP Maven Dependency Check
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * 0'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
check-dependencies:
|
||||
name: Check dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
cache: 'maven'
|
||||
- name: Cache NVD DB
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||
key: dependency-check-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
dependency-check
|
||||
env:
|
||||
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
|
||||
- name: Run org.owasp:dependency-check plugin
|
||||
id: dependency-check
|
||||
continue-on-error: true
|
||||
run: mvn -B validate -Pdependency-check
|
||||
env:
|
||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
||||
- name: Upload report on failure
|
||||
if: steps.dependency-check.outcome == 'failure'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dependency-check-report
|
||||
path: target/dependency-check-report.html
|
||||
if-no-files-found: error
|
||||
- name: Slack Notification on regular check
|
||||
if: github.event_name == 'schedule' && steps.dependency-check.outcome == 'failure'
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: false
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "Vulnerabilities in ${{ github.event.repository.name }} detected."
|
||||
SLACK_MESSAGE: "Download the <https://github.com/${{ github.repository }}/actions/run/${{ github.run_id }}|report> for more details."
|
||||
SLACK_FOOTER: false
|
||||
MSG_MINIMAL: true
|
2
.github/workflows/dl-stats.yml
vendored
2
.github/workflows/dl-stats.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- name: Get download count of latest releases
|
||||
id: get-stats
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const query = `query($owner:String!, $name:String!) {
|
||||
|
2
.github/workflows/error-db.yml
vendored
2
.github/workflows/error-db.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
- name: Query Discussion Data
|
||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
||||
id: query-data
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||
|
2
.github/workflows/get-version.yml
vendored
2
.github/workflows/get-version.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
4
.github/workflows/mac-dmg.yml
vendored
4
.github/workflows/mac-dmg.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,mac -DskipTests
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
|
4
.github/workflows/pullrequest.yml
vendored
4
.github/workflows/pullrequest.yml
vendored
@ -18,10 +18,10 @@ jobs:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
29
.github/workflows/release-check.yml
vendored
29
.github/workflows/release-check.yml
vendored
@ -10,12 +10,22 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 21
|
||||
|
||||
jobs:
|
||||
release-check-precondition:
|
||||
check-preconditions:
|
||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- id: validate-pom-version
|
||||
name: Validate POM version
|
||||
run: |
|
||||
@ -37,4 +47,19 @@ jobs:
|
||||
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\"/>" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
||||
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
- name: Cache NVD DB
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||
key: dependency-check-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
dependency-check
|
||||
env:
|
||||
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
|
||||
- name: Run org.owasp:dependency-check plugin
|
||||
id: dependency-check
|
||||
continue-on-error: true
|
||||
run: mvn -B verify -Pdependency-check -DskipTests
|
||||
env:
|
||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
6
.github/workflows/win-exe.yml
vendored
6
.github/workflows/win-exe.yml
vendored
@ -41,7 +41,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@ -73,7 +73,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pdependency-check,win -DskipTests
|
||||
run: mvn -B clean package -Pwin -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@ -275,7 +275,7 @@ jobs:
|
||||
path: dist/win/bundle/resources
|
||||
- name: Strip version info from msi file name
|
||||
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
@ -86,7 +86,7 @@ For more information on the security details visit [cryptomator.org](https://doc
|
||||
|
||||
### Dependencies
|
||||
|
||||
* JDK 19 (e.g. temurin)
|
||||
* JDK 21 (e.g. temurin, zulu)
|
||||
* Maven 3
|
||||
|
||||
### Run Maven
|
||||
|
@ -66,6 +66,7 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2023-12-05" version="1.11.1"/>
|
||||
<release date="2023-11-08" version="1.11.0"/>
|
||||
<release date="2023-09-20" version="1.10.1"/>
|
||||
<release date="2023-09-11" version="1.10.0"/>
|
||||
|
38
pom.xml
38
pom.xml
@ -33,44 +33,44 @@
|
||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.6.7</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.6.8</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>4.0.0-beta4</cryptomator.fuse.version>
|
||||
<cryptomator.integrations.linux.version>1.4.0</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>4.0.0</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<commons-lang3.version>3.13.0</commons-lang3.version>
|
||||
<dagger.version>2.48.1</dagger.version>
|
||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||
<dagger.version>2.49</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
<jackson.version>2.15.3</jackson.version>
|
||||
<javafx.version>20.0.2</javafx.version>
|
||||
<jackson.version>2.16.0</jackson.version>
|
||||
<javafx.version>21.0.1</javafx.version>
|
||||
<jwt.version>4.4.0</jwt.version>
|
||||
<nimbus-jose.version>9.37</nimbus-jose.version>
|
||||
<logback.version>1.4.11</logback.version>
|
||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
||||
<logback.version>1.4.14</logback.version>
|
||||
<slf4j.version>2.0.9</slf4j.version>
|
||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
||||
<zxcvbn.version>1.8.2</zxcvbn.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.10.0</junit.jupiter.version>
|
||||
<mockito.version>5.6.0</mockito.version>
|
||||
<junit.jupiter.version>5.10.1</junit.jupiter.version>
|
||||
<mockito.version>5.8.0</mockito.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>24.0.1</jetbrains.annotations.version>
|
||||
<dependency-check.version>8.4.0</dependency-check.version>
|
||||
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>9.0.7</dependency-check.version>
|
||||
<jacoco.version>0.8.11</jacoco.version>
|
||||
<license-generator.version>2.2.0</license-generator.version>
|
||||
<license-generator.version>2.3.0</license-generator.version>
|
||||
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
|
||||
<mvn-compiler.version>3.11.0</mvn-compiler.version>
|
||||
<mvn-compiler.version>3.12.1</mvn-compiler.version>
|
||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
||||
<mvn-dependency.version>3.6.0</mvn-dependency.version>
|
||||
<mvn-surefire.version>3.1.2</mvn-surefire.version>
|
||||
<mvn-dependency.version>3.6.1</mvn-dependency.version>
|
||||
<mvn-surefire.version>3.2.3</mvn-surefire.version>
|
||||
<mvn-jar.version>3.3.0</mvn-jar.version>
|
||||
</properties>
|
||||
|
||||
@ -460,17 +460,19 @@
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
<configuration>
|
||||
<cveValidForHours>24</cveValidForHours>
|
||||
<nvdValidForHours>24</nvdValidForHours>
|
||||
<failBuildOnCVSS>0</failBuildOnCVSS>
|
||||
<skipTestScope>true</skipTestScope>
|
||||
<detail>true</detail>
|
||||
<suppressionFile>suppression.xml</suppressionFile>
|
||||
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
@ -5,10 +5,8 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.common;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.keychain.KeychainModule;
|
||||
import org.cryptomator.common.mount.MountModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
@ -22,8 +20,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
@ -136,13 +132,4 @@ public abstract class CommonsModule {
|
||||
LOG.error("Uncaught exception in " + thread.getName(), throwable);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
||||
return settings.port.map(port -> {
|
||||
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
||||
return InetSocketAddress.createUnresolved(host, settings.port.intValue());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
public record ActualMountService(MountService service, boolean isDesired) {
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import org.cryptomator.integrations.mount.MountFailedException;
|
||||
|
||||
/**
|
||||
* Thrown by {@link Mounter} to indicate that the selected mount service can not be used
|
||||
* due to incompatibilities with a different mount service that is already in use.
|
||||
*/
|
||||
public class ConflictingMountServiceException extends MountFailedException {
|
||||
|
||||
public ConflictingMountServiceException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -4,21 +4,18 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.integrations.mount.Mount;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Module
|
||||
public class MountModule {
|
||||
|
||||
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
|
||||
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static List<MountService> provideSupportedMountServices() {
|
||||
@ -27,46 +24,18 @@ public class MountModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("FUPFMS")
|
||||
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
|
||||
return new AtomicReference<>(null);
|
||||
static ObservableValue<MountService> provideDefaultMountService(List<MountService> mountProviders, Settings settings) {
|
||||
var fallbackProvider = mountProviders.stream().findFirst().get(); //there should always be a mount provider, at least webDAV
|
||||
return ObservableUtil.mapWithDefault(settings.mountService, //
|
||||
serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), //
|
||||
fallbackProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
|
||||
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
||||
|
||||
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
|
||||
desiredServiceImpl -> { //
|
||||
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
|
||||
var targetedService = serviceFromSettings.orElse(fallbackProvider);
|
||||
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
|
||||
}, //
|
||||
() -> { //
|
||||
return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
|
||||
});
|
||||
return observableMountService;
|
||||
@Named("usedMountServices")
|
||||
static Set<MountService> provideSetOfUsedMountServices() {
|
||||
return ConcurrentHashMap.newKeySet();
|
||||
}
|
||||
|
||||
//see https://github.com/cryptomator/cryptomator/issues/2786
|
||||
private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) {
|
||||
//set the first used problematic fuse service if applicable
|
||||
var targetIsProblematicFuse = isProblematicFuseService(targetedService);
|
||||
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
|
||||
firstUsedProblematicFuseMountService.set(targetedService);
|
||||
}
|
||||
|
||||
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
|
||||
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
|
||||
return new ActualMountService(formerSelectedMountService.get(), false);
|
||||
} else {
|
||||
formerSelectedMountService.set(targetedService);
|
||||
return new ActualMountService(targetedService, isDesired);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isProblematicFuseService(MountService service) {
|
||||
return problematicFuseMountServices.contains(service.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
@ -9,11 +9,15 @@ import org.cryptomator.integrations.mount.MountFailedException;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
|
||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
|
||||
@ -24,24 +28,39 @@ import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
|
||||
@Singleton
|
||||
public class Mounter {
|
||||
|
||||
private final Settings settings;
|
||||
// mount providers (key) can not be used if any of the conflicting mount providers (values) are already in use
|
||||
private static final Map<String, Set<String>> CONFLICTING_MOUNT_SERVICES = Map.of(
|
||||
"org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.FuseTMountProvider"),
|
||||
"org.cryptomator.frontend.fuse.mount.FuseTMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider")
|
||||
);
|
||||
|
||||
private final Environment env;
|
||||
private final Settings settings;
|
||||
private final WindowsDriveLetters driveLetters;
|
||||
private final ObservableValue<ActualMountService> mountServiceObservable;
|
||||
private final List<MountService> mountProviders;
|
||||
private final Set<MountService> usedMountServices;
|
||||
private final ObservableValue<MountService> defaultMountService;
|
||||
|
||||
@Inject
|
||||
public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue<ActualMountService> mountServiceObservable) {
|
||||
this.settings = settings;
|
||||
public Mounter(Environment env, //
|
||||
Settings settings, //
|
||||
WindowsDriveLetters driveLetters, //
|
||||
List<MountService> mountProviders, //
|
||||
@Named("usedMountServices") Set<MountService> usedMountServices, //
|
||||
ObservableValue<MountService> defaultMountService) {
|
||||
this.env = env;
|
||||
this.settings = settings;
|
||||
this.driveLetters = driveLetters;
|
||||
this.mountServiceObservable = mountServiceObservable;
|
||||
this.mountProviders = mountProviders;
|
||||
this.usedMountServices = usedMountServices;
|
||||
this.defaultMountService = defaultMountService;
|
||||
}
|
||||
|
||||
private class SettledMounter {
|
||||
|
||||
private MountService service;
|
||||
private MountBuilder builder;
|
||||
private VaultSettings vaultSettings;
|
||||
private final MountService service;
|
||||
private final MountBuilder builder;
|
||||
private final VaultSettings vaultSettings;
|
||||
|
||||
public SettledMounter(MountService service, MountBuilder builder, VaultSettings vaultSettings) {
|
||||
this.service = service;
|
||||
@ -53,8 +72,13 @@ public class Mounter {
|
||||
for (var capability : service.capabilities()) {
|
||||
switch (capability) {
|
||||
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
||||
case LOOPBACK_PORT ->
|
||||
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
|
||||
case LOOPBACK_PORT -> {
|
||||
if (vaultSettings.mountService.getValue() == null) {
|
||||
builder.setLoopbackPort(settings.port.get());
|
||||
} else {
|
||||
builder.setLoopbackPort(vaultSettings.port.get());
|
||||
}
|
||||
}
|
||||
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
||||
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
|
||||
case MOUNT_FLAGS -> {
|
||||
@ -131,13 +155,26 @@ public class Mounter {
|
||||
}
|
||||
|
||||
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
||||
var mountService = this.mountServiceObservable.getValue().service();
|
||||
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
|
||||
|
||||
if (isConflictingMountService(mountService)) {
|
||||
var msg = STR."\{mountService.getClass()} unavailable due to conflict with either of \{CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName())}";
|
||||
throw new ConflictingMountServiceException(msg);
|
||||
}
|
||||
|
||||
usedMountServices.add(mountService);
|
||||
|
||||
var builder = mountService.forFileSystem(cryptoFsRoot);
|
||||
var internal = new SettledMounter(mountService, builder, vaultSettings);
|
||||
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
||||
var cleanup = internal.prepare();
|
||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||
}
|
||||
|
||||
public boolean isConflictingMountService(MountService service) {
|
||||
var conflictingServices = CONFLICTING_MOUNT_SERVICES.getOrDefault(service.getClass().getName(), Set.of());
|
||||
return usedMountServices.stream().map(MountService::getClass).map(Class::getName).anyMatch(conflictingServices::contains);
|
||||
}
|
||||
|
||||
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ package org.cryptomator.common.settings;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
@ -40,6 +39,7 @@ public class VaultSettings {
|
||||
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
||||
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
||||
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
||||
static final int DEFAULT_PORT = 42427;
|
||||
|
||||
private static final Random RNG = new Random();
|
||||
|
||||
@ -56,6 +56,8 @@ public class VaultSettings {
|
||||
public final IntegerProperty autoLockIdleSeconds;
|
||||
public final ObjectProperty<Path> mountPoint;
|
||||
public final StringExpression mountName;
|
||||
public final StringProperty mountService;
|
||||
public final IntegerProperty port;
|
||||
|
||||
VaultSettings(VaultSettingsJson json) {
|
||||
this.id = json.id;
|
||||
@ -70,6 +72,8 @@ public class VaultSettings {
|
||||
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
|
||||
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
|
||||
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||
this.port = new SimpleIntegerProperty(this, "port", json.port);
|
||||
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||
final String name;
|
||||
@ -95,7 +99,7 @@ public class VaultSettings {
|
||||
}
|
||||
|
||||
Observable[] observables() {
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
|
||||
}
|
||||
|
||||
public static VaultSettings withRandomId() {
|
||||
@ -124,6 +128,8 @@ public class VaultSettings {
|
||||
json.autoLockWhenIdle = autoLockWhenIdle.get();
|
||||
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
|
||||
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||
json.mountService = mountService.get();
|
||||
json.port = port.get();
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,12 @@ class VaultSettingsJson {
|
||||
@JsonProperty("autoLockIdleSeconds")
|
||||
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
||||
|
||||
@JsonProperty("mountService")
|
||||
String mountService;
|
||||
|
||||
@JsonProperty("port")
|
||||
int port = VaultSettings.DEFAULT_PORT;
|
||||
|
||||
@Deprecated(since = "1.7.0")
|
||||
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||
String winDriveLetter;
|
||||
|
@ -11,7 +11,6 @@ package org.cryptomator.common.vaults;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.mount.Mounter;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||
@ -73,7 +72,13 @@ public class Vault {
|
||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
|
||||
Vault(VaultSettings vaultSettings, //
|
||||
VaultConfigCache configCache, //
|
||||
AtomicReference<CryptoFileSystem> cryptoFileSystem, //
|
||||
VaultState state, //
|
||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||
VaultStats stats, //
|
||||
Mounter mounter) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.configCache = configCache;
|
||||
this.cryptoFileSystem = cryptoFileSystem;
|
||||
|
@ -45,6 +45,7 @@ public enum FxmlFile {
|
||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
||||
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
||||
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
|
||||
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
|
||||
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
||||
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
|
||||
|
@ -2,17 +2,14 @@ package org.cryptomator.ui.preferences;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.mount.MountModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.integrations.mount.MountCapability;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
@ -21,24 +18,22 @@ import javafx.util.StringConverter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@PreferencesScoped
|
||||
public class VolumePreferencesController implements FxController {
|
||||
|
||||
private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
|
||||
private static final int MIN_PORT = 1024;
|
||||
private static final int MAX_PORT = 65535;
|
||||
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
|
||||
public static final int MIN_PORT = 1024;
|
||||
public static final int MAX_PORT = 65535;
|
||||
|
||||
private final Settings settings;
|
||||
private final ObservableValue<MountService> selectedMountService;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final BooleanExpression loopbackPortSupported;
|
||||
private final ObservableValue<Boolean> loopbackPortSupported;
|
||||
private final ObservableValue<Boolean> mountToDirSupported;
|
||||
private final ObservableValue<Boolean> mountToDriveLetterSupported;
|
||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||
private final ObservableValue<Boolean> readonlySupported;
|
||||
private final ObservableValue<Boolean> fuseRestartRequired;
|
||||
private final Lazy<Application> application;
|
||||
private final List<MountService> mountProviders;
|
||||
public ChoiceBox<MountService> volumeTypeChoiceBox;
|
||||
@ -46,7 +41,10 @@ public class VolumePreferencesController implements FxController {
|
||||
public Button loopbackPortApplyButton;
|
||||
|
||||
@Inject
|
||||
VolumePreferencesController(Settings settings, Lazy<Application> application, List<MountService> mountProviders, @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) {
|
||||
VolumePreferencesController(Settings settings, //
|
||||
Lazy<Application> application, //
|
||||
List<MountService> mountProviders, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.settings = settings;
|
||||
this.application = application;
|
||||
this.mountProviders = mountProviders;
|
||||
@ -54,17 +52,11 @@ public class VolumePreferencesController implements FxController {
|
||||
|
||||
var fallbackProvider = mountProviders.stream().findFirst().orElse(null);
|
||||
this.selectedMountService = ObservableUtil.mapWithDefault(settings.mountService, serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), fallbackProvider);
|
||||
this.loopbackPortSupported = BooleanExpression.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT)));
|
||||
this.loopbackPortSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT));
|
||||
this.mountToDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT) || s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR));
|
||||
this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
||||
this.fuseRestartRequired = selectedMountService.map(s -> {//
|
||||
return firstUsedProblematicFuseMountService.get() != null //
|
||||
&& MountModule.isProblematicFuseService(s) //
|
||||
&& !firstUsedProblematicFuseMountService.get().equals(s);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@ -101,12 +93,12 @@ public class VolumePreferencesController implements FxController {
|
||||
|
||||
/* Property Getters */
|
||||
|
||||
public BooleanExpression loopbackPortSupportedProperty() {
|
||||
public ObservableValue<Boolean> loopbackPortSupportedProperty() {
|
||||
return loopbackPortSupported;
|
||||
}
|
||||
|
||||
public boolean isLoopbackPortSupported() {
|
||||
return loopbackPortSupported.get();
|
||||
return loopbackPortSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> readonlySupportedProperty() {
|
||||
@ -141,14 +133,6 @@ public class VolumePreferencesController implements FxController {
|
||||
return mountFlagsSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> fuseRestartRequiredProperty() {
|
||||
return fuseRestartRequired;
|
||||
}
|
||||
|
||||
public boolean getFuseRestartRequired() {
|
||||
return fuseRestartRequired.getValue();
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
|
||||
private class MountServiceConverter extends StringConverter<MountService> {
|
||||
|
@ -19,16 +19,8 @@ import java.util.concurrent.Future;
|
||||
@Subcomponent(modules = {UnlockModule.class})
|
||||
public interface UnlockComponent {
|
||||
|
||||
ExecutorService defaultExecutorService();
|
||||
|
||||
UnlockWorkflow unlockWorkflow();
|
||||
|
||||
default Future<Boolean> startUnlockWorkflow() {
|
||||
UnlockWorkflow workflow = unlockWorkflow();
|
||||
defaultExecutorService().submit(workflow);
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
UnlockComponent create(@BindsInstance @UnlockWindow Vault vault, @BindsInstance @Named("unlockWindowOwner") @Nullable Stage owner);
|
||||
|
@ -81,6 +81,13 @@ abstract class UnlockModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART)
|
||||
@UnlockScoped
|
||||
static Scene provideRestartRequiredScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_REQUIRES_RESTART);
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
@Binds
|
||||
@ -93,4 +100,9 @@ abstract class UnlockModule {
|
||||
@FxControllerKey(UnlockInvalidMountPointController.class)
|
||||
abstract FxController bindUnlockInvalidMountPointController(UnlockInvalidMountPointController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(UnlockRequiresRestartController.class)
|
||||
abstract FxController bindUnlockRequiresRestartController(UnlockRequiresRestartController controller);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
package org.cryptomator.ui.unlock;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@UnlockScoped
|
||||
public class UnlockRequiresRestartController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Vault vault;
|
||||
|
||||
@Inject
|
||||
UnlockRequiresRestartController(@UnlockWindow Stage window, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationWindows appWindows, //
|
||||
@UnlockWindow Vault vault) {
|
||||
this.window = window;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.appWindows = appWindows;
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
window.setTitle(String.format(resourceBundle.getString("unlock.error.title"), vault.getDisplayName()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void closeAndOpenVaultOptions() {
|
||||
appWindows.showVaultOptionsWindow(vault, SelectedVaultOptionsTab.MOUNT);
|
||||
window.close();
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package org.cryptomator.ui.unlock;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.mount.ConflictingMountServiceException;
|
||||
import org.cryptomator.common.mount.IllegalMountPointException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
@ -29,7 +29,7 @@ import java.io.IOException;
|
||||
* This class runs the unlock process and controls when to display which UI.
|
||||
*/
|
||||
@UnlockScoped
|
||||
public class UnlockWorkflow extends Task<Boolean> {
|
||||
public class UnlockWorkflow extends Task<Void> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class);
|
||||
|
||||
@ -38,42 +38,44 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
private final VaultService vaultService;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final Lazy<Scene> invalidMountPointScene;
|
||||
private final Lazy<Scene> restartRequiredScene;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final KeyLoadingStrategy keyLoadingStrategy;
|
||||
private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
|
||||
|
||||
@Inject
|
||||
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
|
||||
UnlockWorkflow(@UnlockWindow Stage window, //
|
||||
@UnlockWindow Vault vault, //
|
||||
VaultService vaultService, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
|
||||
FxApplicationWindows appWindows, //
|
||||
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
|
||||
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.vaultService = vaultService;
|
||||
this.successScene = successScene;
|
||||
this.invalidMountPointScene = invalidMountPointScene;
|
||||
this.restartRequiredScene = restartRequiredScene;
|
||||
this.appWindows = appWindows;
|
||||
this.keyLoadingStrategy = keyLoadingStrategy;
|
||||
this.illegalMountPointException = illegalMountPointException;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean call() throws InterruptedException, IOException, CryptoException, MountFailedException {
|
||||
try {
|
||||
attemptUnlock();
|
||||
return true;
|
||||
} catch (UnlockCancelledException e) {
|
||||
cancel(false); // set Tasks state to cancelled
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptUnlock() throws IOException, CryptoException, MountFailedException {
|
||||
protected Void call() throws InterruptedException, IOException, CryptoException, MountFailedException {
|
||||
try {
|
||||
keyLoadingStrategy.use(vault::unlock);
|
||||
return null;
|
||||
} catch (UnlockCancelledException e) {
|
||||
cancel(false); // set Tasks state to cancelled
|
||||
return null;
|
||||
} catch (IOException | RuntimeException | MountFailedException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Throwables.propagateIfPossible(e, IOException.class);
|
||||
Throwables.propagateIfPossible(e, CryptoException.class);
|
||||
Throwables.propagateIfPossible(e, IllegalMountPointException.class);
|
||||
Throwables.propagateIfPossible(e, MountFailedException.class);
|
||||
throw new IllegalStateException("unexpected exception type", e);
|
||||
throw new IllegalStateException("Unexpected exception type", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +87,13 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
});
|
||||
}
|
||||
|
||||
private void handleConflictingMountServiceException() {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(restartRequiredScene.get());
|
||||
window.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleGenericError(Throwable e) {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
@ -113,10 +122,10 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
protected void failed() {
|
||||
LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
|
||||
Throwable throwable = super.getException();
|
||||
if(throwable instanceof IllegalMountPointException impe) {
|
||||
handleIllegalMountPointError(impe);
|
||||
} else {
|
||||
handleGenericError(throwable);
|
||||
switch (throwable) {
|
||||
case IllegalMountPointException e -> handleIllegalMountPointError(e);
|
||||
case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
|
||||
default -> handleGenericError(throwable);
|
||||
}
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
@ -1,18 +1,25 @@
|
||||
package org.cryptomator.ui.vaultoptions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.cryptomator.common.mount.ActualMountService;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.mount.Mounter;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.integrations.mount.MountCapability;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.preferences.VolumePreferencesController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.RadioButton;
|
||||
@ -26,6 +33,8 @@ import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
@ -36,14 +45,21 @@ public class MountOptionsController implements FxController {
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Lazy<Application> application;
|
||||
|
||||
private final ObservableValue<String> defaultMountFlags;
|
||||
private final ObservableValue<Boolean> mountpointDirSupported;
|
||||
private final ObservableValue<Boolean> mountpointDriveLetterSupported;
|
||||
private final ObservableValue<Boolean> readOnlySupported;
|
||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||
private final ObservableValue<Boolean> defaultMountServiceSelected;
|
||||
private final ObservableValue<String> directoryPath;
|
||||
private final FxApplicationWindows applicationWindows;
|
||||
private final List<MountService> mountProviders;
|
||||
private final ObservableValue<MountService> defaultMountService;
|
||||
private final ObservableValue<MountService> selectedMountService;
|
||||
private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
|
||||
private final ObservableValue<Boolean> loopbackPortChangeable;
|
||||
|
||||
|
||||
//-- FXML objects --
|
||||
@ -56,30 +72,58 @@ public class MountOptionsController implements FxController {
|
||||
public RadioButton mountPointDirBtn;
|
||||
public TextField directoryPathField;
|
||||
public ChoiceBox<Path> driveLetterSelection;
|
||||
public ChoiceBox<MountService> vaultVolumeTypeChoiceBox;
|
||||
public TextField vaultLoopbackPortField;
|
||||
public Button vaultLoopbackPortApplyButton;
|
||||
|
||||
|
||||
@Inject
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObservableValue<ActualMountService> mountService, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, FxApplicationWindows applicationWindows) {
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, //
|
||||
@VaultOptionsWindow Vault vault, //
|
||||
WindowsDriveLetters windowsDriveLetters, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationWindows applicationWindows, //
|
||||
Lazy<Application> application, //
|
||||
List<MountService> mountProviders, //
|
||||
Mounter mounter, //
|
||||
ObservableValue<MountService> defaultMountService) {
|
||||
this.window = window;
|
||||
this.vaultSettings = vault.getVaultSettings();
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.defaultMountFlags = mountService.map(as -> {
|
||||
if (as.service().hasCapability(MountCapability.MOUNT_FLAGS)) {
|
||||
return as.service().getDefaultMountFlags();
|
||||
this.applicationWindows = applicationWindows;
|
||||
this.directoryPath = vault.getVaultSettings().mountPoint.map(p -> isDriveLetter(p) ? null : p.toString());
|
||||
this.application = application;
|
||||
this.mountProviders = mountProviders;
|
||||
this.defaultMountService = defaultMountService;
|
||||
this.selectedMountService = Bindings.createObjectBinding(this::reselectMountService, defaultMountService, vaultSettings.mountService);
|
||||
this.selectedMountServiceRequiresRestart = selectedMountService.map(mounter::isConflictingMountService);
|
||||
|
||||
this.defaultMountFlags = selectedMountService.map(s -> {
|
||||
if (s.hasCapability(MountCapability.MOUNT_FLAGS)) {
|
||||
return s.getDefaultMountFlags();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
this.mountpointDirSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || as.service().hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountpointDriveLetterSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.mountFlagsSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.readOnlySupported = mountService.map(as -> as.service().hasCapability(MountCapability.READ_ONLY));
|
||||
this.directoryPath = vault.getVaultSettings().mountPoint.map(p -> isDriveLetter(p) ? null : p.toString());
|
||||
this.applicationWindows = applicationWindows;
|
||||
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true);
|
||||
this.readOnlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
||||
this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null);
|
||||
}
|
||||
|
||||
private MountService reselectMountService() {
|
||||
var desired = vaultSettings.mountService.getValue();
|
||||
var defaultMS = defaultMountService.getValue();
|
||||
return mountProviders.stream().filter(s -> s.getClass().getName().equals(desired)).findFirst().orElse(defaultMS);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
defaultMountService.addListener((_, _, _) -> vaultVolumeTypeChoiceBox.setConverter(new MountServiceConverter()));
|
||||
|
||||
// readonly:
|
||||
readOnlyCheckbox.selectedProperty().bindBidirectional(vaultSettings.usesReadOnlyMode);
|
||||
|
||||
@ -106,6 +150,20 @@ public class MountOptionsController implements FxController {
|
||||
mountPointToggleGroup.selectToggle(mountPointDirBtn);
|
||||
}
|
||||
mountPointToggleGroup.selectedToggleProperty().addListener(this::selectedToggleChanged);
|
||||
|
||||
vaultVolumeTypeChoiceBox.getItems().add(null);
|
||||
vaultVolumeTypeChoiceBox.getItems().addAll(mountProviders);
|
||||
vaultVolumeTypeChoiceBox.setConverter(new MountServiceConverter());
|
||||
vaultVolumeTypeChoiceBox.getSelectionModel().select(isDefaultMountServiceSelected() ? null : selectedMountService.getValue());
|
||||
vaultVolumeTypeChoiceBox.valueProperty().addListener((_, _, newProvider) -> {
|
||||
var toSet = Optional.ofNullable(newProvider).map(nP -> nP.getClass().getName()).orElse(null);
|
||||
vaultSettings.mountService.set(toSet);
|
||||
});
|
||||
|
||||
vaultLoopbackPortField.setText(String.valueOf(vaultSettings.port.get()));
|
||||
vaultLoopbackPortApplyButton.visibleProperty().bind(vaultSettings.port.asString().isNotEqualTo(vaultLoopbackPortField.textProperty()));
|
||||
vaultLoopbackPortApplyButton.disableProperty().bind(Bindings.createBooleanBinding(this::validateLoopbackPort, vaultLoopbackPortField.textProperty()).not());
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -229,6 +287,26 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
}
|
||||
|
||||
public void openDocs() {
|
||||
application.get().getHostServices().showDocument(VolumePreferencesController.DOCS_MOUNTING_URL);
|
||||
}
|
||||
|
||||
private boolean validateLoopbackPort() {
|
||||
try {
|
||||
int port = Integer.parseInt(vaultLoopbackPortField.getText());
|
||||
return port == 0 // choose port automatically
|
||||
|| port >= VolumePreferencesController.MIN_PORT && port <= VolumePreferencesController.MAX_PORT; // port within range
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void doChangeLoopbackPort() {
|
||||
if (validateLoopbackPort()) {
|
||||
vaultSettings.port.set(Integer.parseInt(vaultLoopbackPortField.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
private static class NoDirSelectedException extends Exception {}
|
||||
//@formatter:on
|
||||
@ -243,6 +321,14 @@ public class MountOptionsController implements FxController {
|
||||
return mountFlagsSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> defaultMountServiceSelectedProperty() {
|
||||
return defaultMountServiceSelected;
|
||||
}
|
||||
|
||||
public boolean isDefaultMountServiceSelected() {
|
||||
return defaultMountServiceSelected.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> mountpointDirSupportedProperty() {
|
||||
return mountpointDirSupported;
|
||||
}
|
||||
@ -274,4 +360,37 @@ public class MountOptionsController implements FxController {
|
||||
public String getDirectoryPath() {
|
||||
return directoryPath.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> selectedMountServiceRequiresRestartProperty() {
|
||||
return selectedMountServiceRequiresRestart;
|
||||
}
|
||||
|
||||
public boolean getSelectedMountServiceRequiresRestart() {
|
||||
return selectedMountServiceRequiresRestart.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> loopbackPortChangeableProperty() {
|
||||
return loopbackPortChangeable;
|
||||
}
|
||||
|
||||
public boolean isLoopbackPortChangeable() {
|
||||
return loopbackPortChangeable.getValue();
|
||||
}
|
||||
|
||||
private class MountServiceConverter extends StringConverter<MountService> {
|
||||
|
||||
@Override
|
||||
public String toString(MountService provider) {
|
||||
if (provider == null) {
|
||||
return String.format(resourceBundle.getString("vaultOptions.mount.volumeType.default"), defaultMountService.getValue().displayName());
|
||||
} else {
|
||||
return provider.displayName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MountService fromString(String string) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,6 @@
|
||||
</Hyperlink>
|
||||
</HBox>
|
||||
|
||||
<Label styleClass="label-red" text="%preferences.volume.fuseRestartRequired" visible="${controller.fuseRestartRequired}" managed="${controller.fuseRestartRequired}"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}">
|
||||
<Label text="%preferences.volume.tcp.port"/>
|
||||
<NumericTextField fx:id="loopbackPortField"/>
|
||||
|
58
src/main/resources/fxml/unlock_requires_restart.fxml
Normal file
58
src/main/resources/fxml/unlock_requires_restart.fxml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.unlock.UnlockRequiresRestartController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-red" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="TIMES" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%unlock.error.restartRequired.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<Label text="%unlock.error.restartRequired.description" wrapText="true" textAlignment="LEFT"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
|
||||
<buttons>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
|
||||
<Button text="%main.vaultlist.contextMenu.vaultoptions" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenVaultOptions">
|
||||
<tooltip>
|
||||
<Tooltip text="%main.vaultlist.contextMenu.vaultoptions"/>
|
||||
</tooltip>
|
||||
</Button>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import org.cryptomator.ui.controls.NumericTextField?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
@ -10,9 +11,9 @@
|
||||
<?import javafx.scene.control.RadioButton?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.ToggleGroup?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
||||
@ -24,11 +25,35 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<TextFlow>
|
||||
<Label text="%vaultOptions.mount.info"/>
|
||||
<Label text=" "/>
|
||||
<Hyperlink styleClass="hyperlink-underline" text="%vaultOptions.mount.linkToPreferences" onAction="#openVolumePreferences" wrapText="true"/>
|
||||
</TextFlow>
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="%vaultOptions.mount.volume.type"/>
|
||||
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
|
||||
</tooltip>
|
||||
</Hyperlink>
|
||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
|
||||
</tooltip>
|
||||
</Hyperlink>
|
||||
</HBox>
|
||||
|
||||
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
|
||||
<Label text="%vaultOptions.mount.volume.tcp.port"/>
|
||||
<NumericTextField fx:id="vaultLoopbackPortField"/>
|
||||
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
||||
</HBox>
|
||||
|
||||
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlySupported}" managed="${controller.readOnlySupported}"/>
|
||||
|
||||
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
||||
|
@ -142,6 +142,9 @@ unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Your vault could not be mounted to the path "%s". Please try again or choose a different path.
|
||||
unlock.error.customPath.description.notEmptyDir=The custom mount path "%s" is not an empty folder. Please choose an empty folder and try again.
|
||||
unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %2$s
|
||||
unlock.error.restartRequired.message=Unable to unlock vault
|
||||
unlock.error.restartRequired.description=Change the volume type in vault options or restart Cryptomator.
|
||||
unlock.error.title=Unlock "%s" failed
|
||||
## Hub
|
||||
hub.noKeychain.message=Unable to access device key
|
||||
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
|
||||
@ -295,11 +298,11 @@ preferences.interface.showMinimizeButton=Show minimize button
|
||||
preferences.interface.showTrayIcon=Show tray icon (requires restart)
|
||||
## Volume
|
||||
preferences.volume=Virtual Drive
|
||||
preferences.volume.type=Volume Type
|
||||
preferences.volume.type=Default Volume Type
|
||||
preferences.volume.type.automatic=Automatic
|
||||
preferences.volume.docsTooltip=Open the documentation to learn more about the different volume types.
|
||||
preferences.volume.fuseRestartRequired=To apply the changes, Cryptomator needs to be restarted.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.tcp.port=Default TCP Port
|
||||
preferences.volume.supportedFeatures=The chosen volume type supports the following features:
|
||||
preferences.volume.feature.mountAuto=Automatic mount point selection
|
||||
preferences.volume.feature.mountToDir=Custom directory as mount point
|
||||
@ -438,8 +441,7 @@ vaultOptions.general.startHealthCheckBtn=Start Health Check
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Mounting
|
||||
vaultOptions.mount.info=Options depend on the selected volume type.
|
||||
vaultOptions.mount.linkToPreferences=Open virtual drive preferences
|
||||
vaultOptions.mount.info=Open virtual drive preferences to change default settings.
|
||||
vaultOptions.mount.readonly=Read-only
|
||||
vaultOptions.mount.customMountFlags=Custom mount flags
|
||||
vaultOptions.mount.winDriveLetterOccupied=occupied
|
||||
@ -449,6 +451,10 @@ vaultOptions.mount.mountPoint.driveLetter=Use assigned drive letter
|
||||
vaultOptions.mount.mountPoint.custom=Use chosen directory
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Pick a directory
|
||||
vaultOptions.mount.volumeType.default=Default (%s)
|
||||
vaultOptions.mount.volumeType.restartRequired=To use this volume type, Cryptomator needs to be restarted.
|
||||
vaultOptions.mount.volume.tcp.port=TCP Port
|
||||
vaultOptions.mount.volume.type=Volume Type
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Password
|
||||
vaultOptions.masterkey.changePasswordBtn=Change Password
|
||||
|
@ -169,6 +169,10 @@ hub.registerFailed.description=В процеса на именуване е до
|
||||
hub.unauthorized.message=Отказан достъп
|
||||
hub.unauthorized.description=Устройството не е упълномощено за достъп до това хранилище. Поискайте достъп от собственика.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Необходимо действие
|
||||
hub.requireAccountInit.description.0=За да продължите завършене необходимите стъпки в
|
||||
hub.requireAccountInit.description.1=профила в Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Лиценза за Hub е недействителен
|
||||
hub.invalidLicense.description=Лиценза на екземпляра на Концентратора на Криптоматор който вие използвате е лиценз. Информирайте администратора на Концентратора, за да поднови или надгради лиценза.
|
||||
|
@ -170,6 +170,8 @@ hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
||||
hub.unauthorized.description=Η συσκευή σας δεν έχει ακόμη εξουσιοδοτηθεί να έχει πρόσβαση σε αυτή την κρύπτη. Ζητήστε από τον κάτοχο της κρύπτης να την εξουσιοδοτήσει.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
||||
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
||||
hub.requireAccountInit.description.1=προφίλ χρήστη Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Μη έγκυρη Άδεια Hub
|
||||
|
@ -169,8 +169,10 @@ hub.registerFailed.description=Ocurrió un error en el nombramiento. Para más d
|
||||
hub.unauthorized.message=Acceso denegado
|
||||
hub.unauthorized.description=Su dispositivo aún no ha sido autorizado para acceder a esta bóveda. Pídale al propietario de la bóveda que lo autorice.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Acción requerida
|
||||
hub.requireAccountInit.description.0=Para continuar, por favor complete los pasos necesarios en su
|
||||
hub.requireAccountInit.description.1=Perfil de usuario del Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licencia del Hub inválida
|
||||
hub.invalidLicense.description=Su instancia del Hub de Cryptomator tiene una licencia inválida. Informe a un administrador del Hub para actualizar o renovar la licencia.
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Pinoproseso ang tugon…
|
||||
hub.receive.description=Ang Cryptomator ay tumatanggap at nagpoproseso ng tugon mula sa Hub. Mangyaring maghintay.
|
||||
### Register Device
|
||||
hub.register.message=Bagong Device
|
||||
hub.register.description=Ito ang unang Hub access mula sa device na ito. Mangyaring pahintulutan ito gamit ang iyong Account Key.
|
||||
hub.register.nameLabel=Pangalan ng device
|
||||
hub.register.invalidAccountKeyLabel=Di-wastong Account Key
|
||||
hub.register.occupiedMsg=Ang pangalan ay nagamit na
|
||||
hub.register.registerBtn=Kumpirmahin
|
||||
### Registration Success
|
||||
@ -167,6 +169,10 @@ hub.registerFailed.description=Nagkaroon ng error sa proseso ng pagbibigay ng pa
|
||||
hub.unauthorized.message=Walang pahintulot
|
||||
hub.unauthorized.description=Hindi pa pinahihintulutan ang iyong device na i-access ang vault na ito. Hilingin sa may-ari ng vault na pahintulutan ito.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Kinakailangan ang pagkilos
|
||||
hub.requireAccountInit.description.0=Upang magpatuloy, mangyaring kumpletuhin ang mga hakbang na kinakailangan sa iyong
|
||||
hub.requireAccountInit.description.1=Profile ng user ng hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Di-wasto ang Lisensya ng Hub
|
||||
hub.invalidLicense.description=Ang iyong Cryptomator Hub instance ay may di-wastong lisensya. Mangyaring ipagbigay-alam sa administrator ng Hub na mag-upgrade o mag-renew ng lisensya.
|
||||
|
@ -171,7 +171,7 @@ hub.unauthorized.description=Votre appareil n'a pas encore été autorisé à ac
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Action requise
|
||||
hub.requireAccountInit.description.0=Pour continuer, veuillez compléter les étapes requises
|
||||
hub.requireAccountInit.description.1=Profil utilisateur Hub
|
||||
hub.requireAccountInit.description.1=Profil utilisateur de Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licence de Hub invalide
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Válasz feldolgozása…
|
||||
hub.receive.description=Cryptomator fogadja és feldolgozza a Hub válaszát. Kérem, várjon.
|
||||
### Register Device
|
||||
hub.register.message=Új eszköz
|
||||
hub.register.description=Ez az első Hub-hozzáférés erről az eszközről. Kérjük, engedélyezd a Fiókkulcsoddal.
|
||||
hub.register.nameLabel=Készülék neve
|
||||
hub.register.invalidAccountKeyLabel=Érvénytelen fiókkulcs
|
||||
hub.register.occupiedMsg=Ez a név már használatban van
|
||||
hub.register.registerBtn=Megerősítés
|
||||
### Registration Success
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Prosesserer svar…
|
||||
hub.receive.description=Cryptomator mottar og behandler svaret fra Hub. Vennligst vent.
|
||||
### Register Device
|
||||
hub.register.message=Ny Enhet
|
||||
hub.register.description=Dette er den første Hub-tilgangen fra denne enheten. Vennligst autoriser den ved hjelp av kontonøkkelen.
|
||||
hub.register.nameLabel=Enhetsnavn
|
||||
hub.register.invalidAccountKeyLabel=Ugyldig kontonøkkel
|
||||
hub.register.occupiedMsg=Navnet er allerede i bruk
|
||||
hub.register.registerBtn=Bekreft
|
||||
### Registration Success
|
||||
@ -167,6 +169,10 @@ hub.registerFailed.description=Under navngivingsprosessen oppsto det en feilmeld
|
||||
hub.unauthorized.message=Ingen tilgang
|
||||
hub.unauthorized.description=Enheten din har ikke blitt autorisert til å få tilgang til dette hvelvet ennå. Spør hvelveieren om å tillate det.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Påkrevd handling
|
||||
hub.requireAccountInit.description.0=For å fortsette, fullfør trinnene som kreves i din
|
||||
hub.requireAccountInit.description.1=Hub brukerprofil
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub-lisens er ugyldig
|
||||
hub.invalidLicense.description=Cryptomator Hub instansen din har en ugyldig lisens. Vennligst informer en Hub-administrator om å oppgradere eller fornye lisensen.
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Antwoord verwerken…
|
||||
hub.receive.description=Cryptomator ontvangt en verwerkt de reactie van Hub. Een ogenblik geduld.
|
||||
### Register Device
|
||||
hub.register.message=Nieuw apparaat
|
||||
hub.register.description=Dit is de eerste Hub toegang vanaf dit apparaat. Bevestig deze toegang met behulp van uw Account Key.
|
||||
hub.register.nameLabel=Apparaatnaam
|
||||
hub.register.invalidAccountKeyLabel=Ongeldige Account Key
|
||||
hub.register.occupiedMsg=Naam al in gebruik
|
||||
hub.register.registerBtn=Bevestig
|
||||
### Registration Success
|
||||
@ -168,6 +170,8 @@ hub.unauthorized.message=Toegang geweigerd
|
||||
hub.unauthorized.description=Uw apparaat is nog niet gemachtigd om toegang te krijgen tot deze kluis. Vraag de eigenaar van de kluis om toestemming te geven.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Actie vereist
|
||||
hub.requireAccountInit.description.0=Om verder te gaan, gelieve de stappen te voltooien in uw
|
||||
hub.requireAccountInit.description.1=Hub gebruikersprofiel
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub Licentie ongeldig
|
||||
|
@ -99,6 +99,7 @@ unlock.success.revealBtn=ਡਰਾਇਵ ਦਿਖਾਓ
|
||||
### Waiting
|
||||
### Receive Key
|
||||
### Register Device
|
||||
hub.register.registerBtn=ਤਸਦੀਕ
|
||||
### Registration Success
|
||||
### Registration Failed
|
||||
### Unauthorized
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Przetwarzanie odpowiedzi…
|
||||
hub.receive.description=Cryptomator odbiera i przetwarza odpowiedź z Huba, proszę czekać.
|
||||
### Register Device
|
||||
hub.register.message=Nowe Urządzenie
|
||||
hub.register.description=To jest pierwszy dostęp do Huba z tego urządzenia. Proszę autoryzować go za pomocą klucza konta.
|
||||
hub.register.nameLabel=Nazwa urządzenia
|
||||
hub.register.invalidAccountKeyLabel=Błędny klucz konta
|
||||
hub.register.occupiedMsg=Nazwa jest już używana
|
||||
hub.register.registerBtn=Zatwierdź
|
||||
### Registration Success
|
||||
@ -167,6 +169,10 @@ hub.registerFailed.description=Wystąpił błąd podczas ustawiania nazwy. Aby u
|
||||
hub.unauthorized.message=Brak dostępu
|
||||
hub.unauthorized.description=Twoje urządzenie nie zostało jeszcze upoważnione do dostępu do tego sejfu. Poproś właściciela sejfu o autoryzację.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Wymagane działanie
|
||||
hub.requireAccountInit.description.0=Aby kontynuować, wykonaj wymagane kroki w Twoim
|
||||
hub.requireAccountInit.description.1=profilu użytkownika Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Nieważna licencja Huba
|
||||
hub.invalidLicense.description=Twoja instancja Hub ma nieprawidłową licencję. Poproś administratora Hub o uaktualnienie lub odnowienie licencji.
|
||||
|
@ -41,6 +41,7 @@ traymenu.vault.reveal=Revelar
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Adicionar Cofre
|
||||
## New
|
||||
addvaultwizard.new.title=Adicionar novo cofre
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Escolha um nome para o cofre
|
||||
addvaultwizard.new.namePrompt=Nome do Cofre
|
||||
@ -90,6 +91,7 @@ addvault.new.readme.accessLocation.2=Este é o local de acesso do seu cofre.
|
||||
addvault.new.readme.accessLocation.3=Qualquer ficheiro adicionado a este volume será encriptado pelo Cryptomator. Poderá trabalhar nestes normalmente como em qualquer outra unidade/pasta. Esta é apenas uma visualização desencriptada do seu conteúdo, os seus ficheiros continuam encriptados no seu disco rígido.
|
||||
addvault.new.readme.accessLocation.4=Sinta-se livre para remover este ficheiro.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Adicionar cofre existente
|
||||
addvaultwizard.existing.instruction=Escolha o ficheiro "vault.cryptomator" do seu cofre. Se encontrar unicamente o ficheiro "masterkey.cryptomator", selecione-o.
|
||||
addvaultwizard.existing.chooseBtn=Escolher…
|
||||
addvaultwizard.existing.filePickerTitle=Selecionar o ficheiro do cofre
|
||||
@ -166,6 +168,8 @@ hub.unauthorized.message=Acesso negado
|
||||
hub.unauthorized.description=O seu dispositivo ainda não foi autorizado a aceder a este cofre. Peça ao proprietário do cofre para o autorizar.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Ação requerida
|
||||
hub.requireAccountInit.description.0=Para continuar, conclua as etapas necessárias no seu
|
||||
hub.requireAccountInit.description.1=perfil de usuário do Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licença Hub inválida
|
||||
@ -339,6 +343,9 @@ main.vaultlist.contextMenu.unlock=Desbloquear…
|
||||
main.vaultlist.contextMenu.unlockNow=Desbloquear agora
|
||||
main.vaultlist.contextMenu.vaultoptions=Mostrar opções do Cofre
|
||||
main.vaultlist.contextMenu.reveal=Revelar unidade
|
||||
main.vaultlist.addVaultBtn=Adicionar
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Novo cofre...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Cofre Existente...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Obrigado por escolher Cryptomator para proteger os seus ficheiros. Se precisar de alguma ajuda, veja os nossos guias introdutórios:
|
||||
|
@ -170,6 +170,8 @@ hub.unauthorized.message=Acesso negado
|
||||
hub.unauthorized.description=Seu dispositivo ainda não foi autorizado a acessar este cofre. Peça ao proprietário ou a um administrador deste cofre para autorizá-lo.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Ação necessária
|
||||
hub.requireAccountInit.description.0=Para prosseguir, por favor, complete os passos necessários
|
||||
hub.requireAccountInit.description.1=Perfil de usuário do Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licença Invalida
|
||||
|
@ -153,6 +153,7 @@ hub.auth.loginLink=Nu ați fost redirecționat? Apăsați aici pentru a deschide
|
||||
hub.receive.message=Se procesează răspunsul…
|
||||
hub.receive.description=In acest moment Criptomatorul primește și procesează răspunsul de la Hub. Vă rugăm să așteptați.
|
||||
### Register Device
|
||||
hub.register.message=Dispozitiv nou
|
||||
hub.register.nameLabel=Numele dispozitivului
|
||||
hub.register.occupiedMsg=Acest nume este deja utilizat
|
||||
hub.register.registerBtn=Confirmați
|
||||
@ -166,6 +167,10 @@ hub.registerFailed.description=O eroare a fost întâmpinata în procesul de den
|
||||
hub.unauthorized.message=Acces respins
|
||||
hub.unauthorized.description=Dispozitivul dvs. nu a fost autorizat să acceseze acest seif. Solicitați proprietarului seifului să va autorizeze accesul.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Acțiune necesară
|
||||
hub.requireAccountInit.description.0=Pentru a continua, vă rugăm să finalizaţi paşii necesari în
|
||||
hub.requireAccountInit.description.1=Profil utilizator Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Licență de Hub invalidă
|
||||
hub.invalidLicense.description=Instanța Hub are o licență invalidă. Vă rugăm să informați un administrator Hub să actualizeze sau să reînnoiască licența.
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Bearbetar svar…
|
||||
hub.receive.description=Cryptomator tar emot och bearbetar svaret från Hub. Vänligen vänta.
|
||||
### Register Device
|
||||
hub.register.message=Ny enhet
|
||||
hub.register.description=Detta är den första navåtkomsten från den här enheten. Vänligen auktorisera den med din kontonyckel.
|
||||
hub.register.nameLabel=Enhetsnamn
|
||||
hub.register.invalidAccountKeyLabel=Ogiltig kontonyckel
|
||||
hub.register.occupiedMsg=Namnet används redan
|
||||
hub.register.registerBtn=Bekräfta
|
||||
### Registration Success
|
||||
@ -167,6 +169,10 @@ hub.registerFailed.description=Ett fel uppstod i namngivningsprocessen. För mer
|
||||
hub.unauthorized.message=Åtkomst nekad
|
||||
hub.unauthorized.description=Din enhet har ännu inte behörighet att komma åt detta valv. Be valvägaren att godkänna det.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Åtgärd krävs
|
||||
hub.requireAccountInit.description.0=För att fortsätta, vänligen fyll i de steg som krävs i din
|
||||
hub.requireAccountInit.description.1=Hubb användarprofil
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Din Hub-licens är ogiltig
|
||||
hub.invalidLicense.description=Din Cryptomator Hub-instans har en ogiltig licens. Vänligen informera en Hub administratör för att uppgradera eller förnya licensen.
|
||||
|
@ -154,7 +154,9 @@ hub.receive.message=Yanıt işleniyor…
|
||||
hub.receive.description=Cryptomator, Hub'dan yanıtı alıyor ve işliyor. Lütfen bekleyin.
|
||||
### Register Device
|
||||
hub.register.message=Yeni Cihaz
|
||||
hub.register.description=Bu işlem bu cihazdan yapılan ilk Hub erişimidir. Lütfen kurulum kodunuzu kullanarak yetkilendirin.
|
||||
hub.register.nameLabel=Cihaz adı
|
||||
hub.register.invalidAccountKeyLabel=Geçersiz Hesap Anahtarı
|
||||
hub.register.occupiedMsg=Ad zaten kullanımda
|
||||
hub.register.registerBtn=Onayla
|
||||
### Registration Success
|
||||
@ -167,8 +169,10 @@ hub.registerFailed.description=İsimlendirme işleminde bir hata oluştu. Daha f
|
||||
hub.unauthorized.message=Erişim engellendi
|
||||
hub.unauthorized.description=Cihazınıza henüz bu kasaya erişim yetkisi verilmedi. Kasa sahibinden yetkilendirmesini isteyin.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Eylem gerekli
|
||||
hub.requireAccountInit.description.0=Devam etmek için, lütfen gerekli adımları tamamlayın
|
||||
hub.requireAccountInit.description.1=Hub kullanıcı profili
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub Lisansı geçersiz
|
||||
hub.invalidLicense.description=Cryptomator Hub örneğinizde geçersiz bir lisans var. Lisansı yükseltmesi veya yenilemesi için lütfen bir Hub yöneticisini bilgilendirin.
|
||||
|
@ -170,6 +170,8 @@ hub.unauthorized.message=拒绝访问
|
||||
hub.unauthorized.description=您的设备尚未授权访问此保险库,请联系保险库所有者,
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=操作请求
|
||||
hub.requireAccountInit.description.0=要继续,请完成所需的步骤
|
||||
hub.requireAccountInit.description.1=Hub 用户中心
|
||||
hub.requireAccountInit.description.2=。
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub 许可证无效
|
||||
@ -295,7 +297,7 @@ preferences.volume=虚拟磁盘
|
||||
preferences.volume.type=卷类型
|
||||
preferences.volume.type.automatic=自动
|
||||
preferences.volume.docsTooltip=打开文档以了解有关不同卷类型的更多信息
|
||||
preferences.volume.fuseRestartRequired=Cryptomator 需要重新启动以应用更改。
|
||||
preferences.volume.fuseRestartRequired=Cryptomator 需要重新启动以应用更改
|
||||
preferences.volume.tcp.port=TCP 端口
|
||||
preferences.volume.supportedFeatures=选定的卷类型支持以下功能:
|
||||
preferences.volume.feature.mountAuto=自动选择挂载点
|
||||
|
@ -166,6 +166,8 @@ hub.registerFailed.description=命名過程中出現錯誤。更多詳情,請
|
||||
hub.unauthorized.message=拒絕存取
|
||||
hub.unauthorized.description=您的設備權限尚未允許存取檔案庫,請聯絡檔案庫擁有者
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.description.0=請完成您的
|
||||
hub.requireAccountInit.description.1=Hub使用者資料
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Hub 憑證無效
|
||||
hub.invalidLicense.description=此 Cryptomator Hub 實例授權無效,請聯繫管理員升級或續訂授權。
|
||||
|
Loading…
Reference in New Issue
Block a user