mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 02:31:27 +00:00
Merge branch 'release/1.5.15'
This commit is contained in:
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -92,6 +92,16 @@ jobs:
|
||||
* [ ] add Linux appimage, zsync file and signature file
|
||||
* [ ] add Windows installer and signature file
|
||||
* [ ] add MacOs disk image and signature file
|
||||
|
||||
## What's new
|
||||
|
||||
## Bugfixes
|
||||
|
||||
## Misc
|
||||
|
||||
---
|
||||
|
||||
:scroll: A complete list of closed issues is available [here](LINK)
|
||||
draft: true
|
||||
prerelease: false
|
||||
- name: Upload buildkit-linux.zip to GitHub Releases
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,5 +21,6 @@ pom.xml.versionsBackup
|
||||
.idea/compiler.xml
|
||||
.idea/encodings.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/uiDesigner.xml
|
||||
.idea/**/libraries/
|
||||
*.iml
|
||||
@@ -48,7 +48,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
||||
|
||||
## Features
|
||||
|
||||
- Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
|
||||
- Works with Dropbox, Google Drive, OneDrive, MEGA, pCloud, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
|
||||
- Open Source means: No backdoors, control is better than trust
|
||||
- Client-side: No accounts, no data shared with any online service
|
||||
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>main</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.15</version>
|
||||
</parent>
|
||||
<artifactId>buildkit</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>main</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.15</version>
|
||||
</parent>
|
||||
<artifactId>commons</artifactId>
|
||||
<name>Cryptomator Commons</name>
|
||||
|
||||
@@ -5,15 +5,15 @@ import org.cryptomator.common.mountpoint.MountPointChooser;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.cryptomator.frontend.dokany.DokanyMountFailedException;
|
||||
import org.cryptomator.frontend.dokany.Mount;
|
||||
import org.cryptomator.frontend.dokany.MountFactory;
|
||||
import org.cryptomator.frontend.dokany.MountFailedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class DokanyVolume extends AbstractVolume {
|
||||
|
||||
@@ -22,15 +22,13 @@ public class DokanyVolume extends AbstractVolume {
|
||||
private static final String FS_TYPE_NAME = "CryptomatorFS";
|
||||
|
||||
private final VaultSettings vaultSettings;
|
||||
private final MountFactory mountFactory;
|
||||
|
||||
private Mount mount;
|
||||
|
||||
@Inject
|
||||
public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
|
||||
public DokanyVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
|
||||
super(choosers);
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.mountFactory = new MountFactory(executorService);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,11 +37,11 @@ public class DokanyVolume extends AbstractVolume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
|
||||
this.mountPoint = determineMountPoint();
|
||||
try {
|
||||
this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip());
|
||||
} catch (MountFailedException e) {
|
||||
this.mount = MountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip(), onExitAction);
|
||||
} catch (DokanyMountFailedException e) {
|
||||
if (vaultSettings.getCustomMountPath().isPresent()) {
|
||||
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||
import org.cryptomator.common.mountpoint.MountPointChooser;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.cryptomator.frontend.fuse.mount.CommandFailedException;
|
||||
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
||||
import org.cryptomator.frontend.fuse.mount.FuseMountException;
|
||||
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
|
||||
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
|
||||
import org.cryptomator.frontend.fuse.mount.Mount;
|
||||
@@ -20,6 +20,7 @@ import javax.inject.Named;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FuseVolume extends AbstractVolume {
|
||||
@@ -35,13 +36,12 @@ public class FuseVolume extends AbstractVolume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
|
||||
this.mountPoint = determineMountPoint();
|
||||
|
||||
mount(fs.getPath("/"), mountFlags);
|
||||
mount(fs.getPath("/"), mountFlags, onExitAction);
|
||||
}
|
||||
|
||||
private void mount(Path root, String mountFlags) throws VolumeException {
|
||||
private void mount(Path root, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
|
||||
try {
|
||||
Mounter mounter = FuseMountFactory.getMounter();
|
||||
EnvironmentVariables envVars = EnvironmentVariables.create() //
|
||||
@@ -49,8 +49,8 @@ public class FuseVolume extends AbstractVolume {
|
||||
.withMountPoint(mountPoint) //
|
||||
.withFileNameTranscoder(mounter.defaultFileNameTranscoder()) //
|
||||
.build();
|
||||
this.mount = mounter.mount(root, envVars);
|
||||
} catch (CommandFailedException | FuseNotSupportedException e) {
|
||||
this.mount = mounter.mount(root, envVars, onExitAction);
|
||||
} catch ( FuseMountException | FuseNotSupportedException e) {
|
||||
throw new VolumeException("Unable to mount Filesystem", e);
|
||||
}
|
||||
}
|
||||
@@ -91,8 +91,7 @@ public class FuseVolume extends AbstractVolume {
|
||||
public synchronized void unmountForced() throws VolumeException {
|
||||
try {
|
||||
mount.unmountForced();
|
||||
mount.close();
|
||||
} catch (CommandFailedException e) {
|
||||
} catch (FuseMountException e) {
|
||||
throw new VolumeException(e);
|
||||
}
|
||||
cleanupMountPoint();
|
||||
@@ -102,8 +101,7 @@ public class FuseVolume extends AbstractVolume {
|
||||
public synchronized void unmount() throws VolumeException {
|
||||
try {
|
||||
mount.unmount();
|
||||
mount.close();
|
||||
} catch (CommandFailedException e) {
|
||||
} catch (FuseMountException e) {
|
||||
throw new VolumeException(e);
|
||||
}
|
||||
cleanupMountPoint();
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.cryptomator.common.vaults;
|
||||
|
||||
public class LockNotCompletedException extends Exception {
|
||||
|
||||
public LockNotCompletedException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
public LockNotCompletedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
@@ -56,7 +57,7 @@ public class Vault {
|
||||
private final Provider<Volume> volumeProvider;
|
||||
private final StringBinding defaultMountFlags;
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
||||
private final ObjectProperty<VaultState> state;
|
||||
private final VaultState state;
|
||||
private final ObjectProperty<Exception> lastKnownException;
|
||||
private final VaultStats stats;
|
||||
private final StringBinding displayName;
|
||||
@@ -74,7 +75,7 @@ public class Vault {
|
||||
private volatile Volume volume;
|
||||
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.volumeProvider = volumeProvider;
|
||||
this.defaultMountFlags = defaultMountFlags;
|
||||
@@ -104,19 +105,27 @@ public class Vault {
|
||||
if (vaultSettings.usesReadOnlyMode().get()) {
|
||||
flags.add(FileSystemFlags.READONLY);
|
||||
}
|
||||
if (!flags.contains(FileSystemFlags.READONLY) && vaultSettings.filenameLengthLimit().get() == -1) {
|
||||
|
||||
int usedFilenameLengthLimit;
|
||||
var fileSystemCapabilityChecker = new FileSystemCapabilityChecker();
|
||||
if (flags.contains(FileSystemFlags.READONLY)) {
|
||||
usedFilenameLengthLimit = Constants.MAX_CIPHERTEXT_NAME_LENGTH;
|
||||
} else if (vaultSettings.filenameLengthLimit().get() == -1) {
|
||||
LOG.debug("Determining file name length limitations...");
|
||||
int limit = new FileSystemCapabilityChecker().determineSupportedFileNameLength(getPath());
|
||||
vaultSettings.filenameLengthLimit().set(limit);
|
||||
LOG.info("Storing file name length limit of {}", limit);
|
||||
usedFilenameLengthLimit = fileSystemCapabilityChecker.determineSupportedFileNameLength(getPath());
|
||||
vaultSettings.filenameLengthLimit().set(usedFilenameLengthLimit);
|
||||
LOG.info("Storing file name length limit of {}", usedFilenameLengthLimit);
|
||||
} else {
|
||||
usedFilenameLengthLimit = vaultSettings.filenameLengthLimit().get();
|
||||
}
|
||||
assert vaultSettings.filenameLengthLimit().get() > 0;
|
||||
|
||||
assert usedFilenameLengthLimit > 0;
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||
.withPassphrase(passphrase) //
|
||||
.withFlags(flags) //
|
||||
.withMasterkeyFilename(MASTERKEY_FILENAME) //
|
||||
.withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) //
|
||||
.withMaxNameLength(vaultSettings.filenameLengthLimit().get()) //
|
||||
.withMaxNameLength(usedFilenameLengthLimit) //
|
||||
.build();
|
||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||
}
|
||||
@@ -139,7 +148,7 @@ public class Vault {
|
||||
cryptoFileSystem.set(fs);
|
||||
try {
|
||||
volume = volumeProvider.get();
|
||||
volume.mount(fs, getEffectiveMountFlags());
|
||||
volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
|
||||
} catch (Exception e) {
|
||||
destroyCryptoFileSystem();
|
||||
throw e;
|
||||
@@ -149,13 +158,33 @@ public class Vault {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void lock(boolean forced) throws VolumeException {
|
||||
private void lockOnVolumeExit(Throwable t) {
|
||||
LOG.info("Unmounted vault '{}'", getDisplayName());
|
||||
destroyCryptoFileSystem();
|
||||
state.set(VaultState.Value.LOCKED);
|
||||
if (t != null) {
|
||||
LOG.warn("Unexpected unmount and lock of vault " + getDisplayName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void lock(boolean forced) throws VolumeException, LockNotCompletedException {
|
||||
//initiate unmount
|
||||
if (forced && volume.supportsForcedUnmount()) {
|
||||
volume.unmountForced();
|
||||
} else {
|
||||
volume.unmount();
|
||||
}
|
||||
destroyCryptoFileSystem();
|
||||
|
||||
//wait for lockOnVolumeExit to be executed
|
||||
try {
|
||||
boolean locked = state.awaitState(VaultState.Value.LOCKED, 3000, TimeUnit.MILLISECONDS);
|
||||
if (!locked) {
|
||||
throw new LockNotCompletedException("Locking of vault " + this.getDisplayName() + " still in progress.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new LockNotCompletedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reveal(Volume.Revealer vaultRevealer) throws VolumeException {
|
||||
@@ -166,16 +195,12 @@ public class Vault {
|
||||
// Observable Properties
|
||||
// *******************************************************************************
|
||||
|
||||
public ObjectProperty<VaultState> stateProperty() {
|
||||
public VaultState stateProperty() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public VaultState getState() {
|
||||
return state.get();
|
||||
}
|
||||
|
||||
public void setState(VaultState value) {
|
||||
state.setValue(value);
|
||||
public VaultState.Value getState() {
|
||||
return state.getValue();
|
||||
}
|
||||
|
||||
public ObjectProperty<Exception> lastKnownExceptionProperty() {
|
||||
@@ -195,7 +220,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return state.get() == VaultState.LOCKED;
|
||||
return state.get() == VaultState.Value.LOCKED;
|
||||
}
|
||||
|
||||
public BooleanBinding processingProperty() {
|
||||
@@ -203,7 +228,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isProcessing() {
|
||||
return state.get() == VaultState.PROCESSING;
|
||||
return state.get() == VaultState.Value.PROCESSING;
|
||||
}
|
||||
|
||||
public BooleanBinding unlockedProperty() {
|
||||
@@ -211,7 +236,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isUnlocked() {
|
||||
return state.get() == VaultState.UNLOCKED;
|
||||
return state.get() == VaultState.Value.UNLOCKED;
|
||||
}
|
||||
|
||||
public BooleanBinding missingProperty() {
|
||||
@@ -219,7 +244,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isMissing() {
|
||||
return state.get() == VaultState.MISSING;
|
||||
return state.get() == VaultState.Value.MISSING;
|
||||
}
|
||||
|
||||
public BooleanBinding needsMigrationProperty() {
|
||||
@@ -227,7 +252,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isNeedsMigration() {
|
||||
return state.get() == VaultState.NEEDS_MIGRATION;
|
||||
return state.get() == VaultState.Value.NEEDS_MIGRATION;
|
||||
}
|
||||
|
||||
public BooleanBinding unknownErrorProperty() {
|
||||
@@ -235,7 +260,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public boolean isUnknownError() {
|
||||
return state.get() == VaultState.ERROR;
|
||||
return state.get() == VaultState.Value.ERROR;
|
||||
}
|
||||
|
||||
public StringBinding displayNameProperty() {
|
||||
@@ -251,7 +276,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public String getAccessPoint() {
|
||||
if (state.get() == VaultState.UNLOCKED) {
|
||||
if (state.getValue() == VaultState.Value.UNLOCKED) {
|
||||
assert volume != null;
|
||||
return volume.getMountPoint().orElse(Path.of("")).toString();
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface VaultComponent {
|
||||
Builder vaultSettings(VaultSettings vaultSettings);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialVaultState(VaultState vaultState);
|
||||
Builder initialVaultState(VaultState.Value vaultState);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
|
||||
@@ -94,42 +93,43 @@ public class VaultListManager {
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
|
||||
try {
|
||||
VaultState vaultState = determineVaultState(vaultSettings.path().get());
|
||||
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
|
||||
compBuilder.initialVaultState(vaultState);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
|
||||
compBuilder.initialVaultState(VaultState.ERROR);
|
||||
compBuilder.initialVaultState(VaultState.Value.ERROR);
|
||||
compBuilder.initialErrorCause(e);
|
||||
}
|
||||
return compBuilder.build().vault();
|
||||
}
|
||||
|
||||
public static VaultState redetermineVaultState(Vault vault) {
|
||||
VaultState previousState = vault.getState();
|
||||
public static VaultState.Value redetermineVaultState(Vault vault) {
|
||||
VaultState state = vault.stateProperty();
|
||||
VaultState.Value previousState = state.getValue();
|
||||
return switch (previousState) {
|
||||
case LOCKED, NEEDS_MIGRATION, MISSING -> {
|
||||
try {
|
||||
VaultState determinedState = determineVaultState(vault.getPath());
|
||||
vault.setState(determinedState);
|
||||
VaultState.Value determinedState = determineVaultState(vault.getPath());
|
||||
state.set(determinedState);
|
||||
yield determinedState;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
|
||||
vault.setState(VaultState.ERROR);
|
||||
state.set(VaultState.Value.ERROR);
|
||||
vault.setLastKnownException(e);
|
||||
yield VaultState.ERROR;
|
||||
yield VaultState.Value.ERROR;
|
||||
}
|
||||
}
|
||||
case ERROR, UNLOCKED, PROCESSING -> previousState;
|
||||
};
|
||||
}
|
||||
|
||||
private static VaultState determineVaultState(Path pathToVault) throws IOException {
|
||||
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
|
||||
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.MISSING;
|
||||
return VaultState.Value.MISSING;
|
||||
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.NEEDS_MIGRATION;
|
||||
return VaultState.Value.NEEDS_MIGRATION;
|
||||
} else {
|
||||
return VaultState.LOCKED;
|
||||
return VaultState.Value.LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,6 @@ public class VaultModule {
|
||||
return new AtomicReference<>();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@PerVault
|
||||
public ObjectProperty<VaultState> provideVaultState(VaultState initialState) {
|
||||
return new SimpleObjectProperty<>(initialState);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("lastKnownException")
|
||||
@PerVault
|
||||
|
||||
@@ -1,34 +1,141 @@
|
||||
package org.cryptomator.common.vaults;
|
||||
|
||||
public enum VaultState {
|
||||
/**
|
||||
* No vault found at the provided path
|
||||
*/
|
||||
MISSING,
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableObjectValue;
|
||||
import javafx.beans.value.ObservableValueBase;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@PerVault
|
||||
public class VaultState extends ObservableValueBase<VaultState.Value> implements ObservableObjectValue<VaultState.Value> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultState.class);
|
||||
|
||||
public enum Value {
|
||||
/**
|
||||
* No vault found at the provided path
|
||||
*/
|
||||
MISSING,
|
||||
|
||||
/**
|
||||
* Vault requires migration to a newer vault format
|
||||
*/
|
||||
NEEDS_MIGRATION,
|
||||
|
||||
/**
|
||||
* Vault ready to be unlocked
|
||||
*/
|
||||
LOCKED,
|
||||
|
||||
/**
|
||||
* Vault in transition between two other states
|
||||
*/
|
||||
PROCESSING,
|
||||
|
||||
/**
|
||||
* Vault is unlocked
|
||||
*/
|
||||
UNLOCKED,
|
||||
|
||||
/**
|
||||
* Unknown state due to preceeding unrecoverable exceptions.
|
||||
*/
|
||||
ERROR;
|
||||
}
|
||||
|
||||
private final AtomicReference<Value> value;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition valueChanged = lock.newCondition();
|
||||
|
||||
@Inject
|
||||
public VaultState(VaultState.Value initialValue) {
|
||||
this.value = new AtomicReference<>(initialValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault requires migration to a newer vault format
|
||||
* Transitions from <code>fromState</code> to <code>toState</code>.
|
||||
*
|
||||
* @param fromState Previous state
|
||||
* @param toState New state
|
||||
* @return <code>true</code> if successful
|
||||
*/
|
||||
NEEDS_MIGRATION,
|
||||
public boolean transition(Value fromState, Value toState) {
|
||||
Preconditions.checkArgument(fromState != toState, "fromState must be different than toState");
|
||||
boolean success = value.compareAndSet(fromState, toState);
|
||||
if (success) {
|
||||
fireValueChangedEvent();
|
||||
} else {
|
||||
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public void set(Value newState) {
|
||||
var oldState = value.getAndSet(newState);
|
||||
if (oldState != newState) {
|
||||
fireValueChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault ready to be unlocked
|
||||
* Waits for the specified time, until the desired state is reached.
|
||||
*
|
||||
* @param desiredState what state to wait for
|
||||
* @param time the maximum time to wait
|
||||
* @param unit the time unit of the {@code time} argument
|
||||
* @return {@code false} if the waiting time detectably elapsed before reaching {@code desiredState}
|
||||
* @throws InterruptedException if the current thread is interrupted
|
||||
*/
|
||||
LOCKED,
|
||||
public boolean awaitState(Value desiredState, long time, TimeUnit unit) throws InterruptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
long remaining = TimeUnit.NANOSECONDS.convert(time, unit);
|
||||
while (value.get() != desiredState) {
|
||||
if (remaining <= 0L) {
|
||||
return false;
|
||||
}
|
||||
remaining = valueChanged.awaitNanos(remaining);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault in transition between two other states
|
||||
*/
|
||||
PROCESSING,
|
||||
|
||||
/**
|
||||
* Vault is unlocked
|
||||
*/
|
||||
UNLOCKED,
|
||||
|
||||
/**
|
||||
* Unknown state due to preceeding unrecoverable exceptions.
|
||||
*/
|
||||
ERROR;
|
||||
private void signal() {
|
||||
lock.lock();
|
||||
try {
|
||||
valueChanged.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fireValueChangedEvent() {
|
||||
signal();
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
super.fireValueChangedEvent();
|
||||
} else {
|
||||
Platform.runLater(super::fireValueChangedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class VaultStats {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultStats.class);
|
||||
|
||||
private final AtomicReference<CryptoFileSystem> fs;
|
||||
private final ObjectProperty<VaultState> state;
|
||||
private final VaultState state;
|
||||
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
|
||||
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
|
||||
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
|
||||
@@ -41,7 +41,7 @@ public class VaultStats {
|
||||
private final LongProperty filesWritten = new SimpleLongProperty();
|
||||
|
||||
@Inject
|
||||
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
|
||||
VaultStats(AtomicReference<CryptoFileSystem> fs, VaultState state, ExecutorService executor) {
|
||||
this.fs = fs;
|
||||
this.state = state;
|
||||
this.updateService = new UpdateStatsService();
|
||||
@@ -52,13 +52,13 @@ public class VaultStats {
|
||||
}
|
||||
|
||||
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
|
||||
if (VaultState.UNLOCKED.equals(state.get())) {
|
||||
if (VaultState.Value.UNLOCKED == state.get()) {
|
||||
assert fs.get() != null;
|
||||
LOG.debug("start recording stats");
|
||||
updateService.restart();
|
||||
Platform.runLater(() -> updateService.restart());
|
||||
} else {
|
||||
LOG.debug("stop recording stats");
|
||||
updateService.cancel();
|
||||
Platform.runLater(() -> updateService.cancel());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@@ -32,7 +34,7 @@ public interface Volume {
|
||||
* @param fs
|
||||
* @throws IOException
|
||||
*/
|
||||
void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException, InvalidMountPointException;
|
||||
void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws IOException, VolumeException, InvalidMountPointException;
|
||||
|
||||
/**
|
||||
* Reveals the mounted volume.
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class WebDavVolume implements Volume {
|
||||
@@ -31,6 +32,7 @@ public class WebDavVolume implements Volume {
|
||||
private WebDavServer server;
|
||||
private WebDavServletController servlet;
|
||||
private Mounter.Mount mount;
|
||||
private Consumer<Throwable> onExitAction;
|
||||
|
||||
@Inject
|
||||
public WebDavVolume(Provider<WebDavServer> serverProvider, VaultSettings vaultSettings, Settings settings, WindowsDriveLetters windowsDriveLetters) {
|
||||
@@ -41,12 +43,13 @@ public class WebDavVolume implements Volume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
|
||||
startServlet(fs);
|
||||
mountServlet();
|
||||
this.onExitAction = onExitAction;
|
||||
}
|
||||
|
||||
private void startServlet(CryptoFileSystem fs){
|
||||
private void startServlet(CryptoFileSystem fs) {
|
||||
if (server == null) {
|
||||
server = serverProvider.get();
|
||||
}
|
||||
@@ -66,7 +69,7 @@ public class WebDavVolume implements Volume {
|
||||
|
||||
//on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specifc one or there is no free.
|
||||
Supplier<String> driveLetterSupplier;
|
||||
if(System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
|
||||
driveLetterSupplier = () -> windowsDriveLetters.getAvailableDriveLetter().orElse(null);
|
||||
} else {
|
||||
driveLetterSupplier = () -> vaultSettings.winDriveLetter().get();
|
||||
@@ -101,6 +104,7 @@ public class WebDavVolume implements Volume {
|
||||
throw new VolumeException(e);
|
||||
}
|
||||
cleanup();
|
||||
onExitAction.accept(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,6 +115,7 @@ public class WebDavVolume implements Volume {
|
||||
throw new VolumeException(e);
|
||||
}
|
||||
cleanup();
|
||||
onExitAction.accept(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>main</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.15</version>
|
||||
</parent>
|
||||
<artifactId>launcher</artifactId>
|
||||
<name>Cryptomator Launcher</name>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>main</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.15</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Cryptomator</name>
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.0.0-beta1</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>1.3.0</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>1.2.4</cryptomator.dokany.version>
|
||||
<cryptomator.fuse.version>1.3.1</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>1.3.0</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>1.2.0</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<javafx.version>15</javafx.version>
|
||||
<javafx.version>16</javafx.version>
|
||||
<commons-lang3.version>3.11</commons-lang3.version>
|
||||
<jwt.version>3.13.0</jwt.version>
|
||||
<easybind.version>2.1.0</easybind.version>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>main</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.15</version>
|
||||
</parent>
|
||||
<artifactId>ui</artifactId>
|
||||
<name>Cryptomator GUI</name>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -15,21 +15,21 @@ import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -43,55 +43,74 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> chooseNameScene;
|
||||
private final Lazy<Scene> choosePasswordScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final LocationPresets locationPresets;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final StringProperty vaultName;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final BooleanBinding validVaultPath;
|
||||
private final BooleanProperty usePresetPath;
|
||||
private final StringProperty warningText;
|
||||
private final StringProperty statusText;
|
||||
private final ObjectProperty<Node> statusGraphic;
|
||||
|
||||
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
||||
|
||||
//FXML
|
||||
public ToggleGroup predefinedLocationToggler;
|
||||
public RadioButton iclouddriveRadioButton;
|
||||
public RadioButton dropboxRadioButton;
|
||||
public RadioButton gdriveRadioButton;
|
||||
public RadioButton onedriveRadioButton;
|
||||
public RadioButton megaRadioButton;
|
||||
public RadioButton pcloudRadioButton;
|
||||
public RadioButton customRadioButton;
|
||||
public Label vaultPathStatus;
|
||||
public FontAwesome5IconView goodLocation;
|
||||
public FontAwesome5IconView badLocation;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ErrorComponent.Builder errorComponent, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.chooseNameScene = chooseNameScene;
|
||||
this.choosePasswordScene = choosePasswordScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.locationPresets = locationPresets;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vaultName = vaultName;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.validVaultPath = Bindings.createBooleanBinding(this::isValidVaultPath, vaultPath);
|
||||
this.validVaultPath = Bindings.createBooleanBinding(this::validateVaultPathAndSetStatus, this.vaultPath);
|
||||
this.usePresetPath = new SimpleBooleanProperty();
|
||||
this.warningText = new SimpleStringProperty();
|
||||
this.statusText = new SimpleStringProperty();
|
||||
this.statusGraphic = new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
private boolean isValidVaultPath() {
|
||||
return vaultPath.get() != null && Files.notExists(vaultPath.get());
|
||||
private boolean validateVaultPathAndSetStatus() {
|
||||
final Path p = vaultPath.get();
|
||||
if (p == null) {
|
||||
statusText.set("Error: Path is NULL.");
|
||||
statusGraphic.set(badLocation);
|
||||
return false;
|
||||
} else if (!Files.exists(p.getParent())) {
|
||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
|
||||
statusGraphic.set(badLocation);
|
||||
return false;
|
||||
} else if (!Files.isWritable(p.getParent())) {
|
||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
|
||||
statusGraphic.set(badLocation);
|
||||
return false;
|
||||
} else if (!Files.notExists(p)) {
|
||||
statusText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
||||
statusGraphic.set(badLocation);
|
||||
return false;
|
||||
} else {
|
||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsOk"));
|
||||
statusGraphic.set(goodLocation);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
||||
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
||||
vaultPath.addListener(this::vaultPathDidChange);
|
||||
}
|
||||
|
||||
private void vaultPathDidChange(@SuppressWarnings("unused") ObservableValue<? extends Path> observable, @SuppressWarnings("unused") Path oldValue, Path newValue) {
|
||||
if (!Files.notExists(newValue)) {
|
||||
warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
||||
} else {
|
||||
warningText.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
||||
@@ -103,6 +122,10 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
|
||||
} else if (onedriveRadioButton.equals(newValue)) {
|
||||
vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get()));
|
||||
} else if (megaRadioButton.equals(newValue)) {
|
||||
vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get()));
|
||||
} else if (pcloudRadioButton.equals(newValue)) {
|
||||
vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get()));
|
||||
} else if (customRadioButton.equals(newValue)) {
|
||||
vaultPath.set(customVaultPath.resolve(vaultName.get()));
|
||||
}
|
||||
@@ -115,21 +138,10 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void next() {
|
||||
try {
|
||||
// check if we have write access AND the vaultPath doesn't already exist:
|
||||
assert Files.isDirectory(vaultPath.get().getParent());
|
||||
Path createdDir = Files.createDirectory(vaultPath.get());
|
||||
Files.delete(createdDir); // assert: dir exists and is empty
|
||||
if (validateVaultPathAndSetStatus()) {
|
||||
window.setScene(choosePasswordScene.get());
|
||||
} catch (FileAlreadyExistsException e) {
|
||||
LOG.warn("Can not use already existing vault path {}", vaultPath.get());
|
||||
warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
||||
} catch (NoSuchFileException e) {
|
||||
LOG.warn("At least one path component does not exist of path {}", vaultPath.get());
|
||||
warningText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to create and delete directory at chosen vault path.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
} else {
|
||||
validVaultPath.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,19 +191,27 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
return usePresetPath.get();
|
||||
}
|
||||
|
||||
public StringProperty warningTextProperty() {
|
||||
return warningText;
|
||||
public BooleanBinding anyRadioButtonSelectedProperty() {
|
||||
return predefinedLocationToggler.selectedToggleProperty().isNotNull();
|
||||
}
|
||||
|
||||
public String getWarningText() {
|
||||
return warningText.get();
|
||||
public boolean isAnyRadioButtonSelected() {
|
||||
return anyRadioButtonSelectedProperty().get();
|
||||
}
|
||||
|
||||
public BooleanBinding showWarningProperty() {
|
||||
return warningText.isNotEmpty();
|
||||
public StringProperty statusTextProperty() {
|
||||
return statusText;
|
||||
}
|
||||
|
||||
public boolean isShowWarning() {
|
||||
return showWarningProperty().get();
|
||||
public String getStatusText() {
|
||||
return statusText.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Node> statusGraphicProperty() {
|
||||
return statusGraphic;
|
||||
}
|
||||
|
||||
public Node getStatusGraphic() {
|
||||
return statusGraphic.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,21 @@ public class LocationPresets {
|
||||
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
|
||||
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive"};
|
||||
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
|
||||
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
|
||||
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
|
||||
|
||||
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
|
||||
private final ReadOnlyObjectProperty<Path> dropboxLocation;
|
||||
private final ReadOnlyObjectProperty<Path> gdriveLocation;
|
||||
private final ReadOnlyObjectProperty<Path> onedriveLocation;
|
||||
private final ReadOnlyObjectProperty<Path> megaLocation;
|
||||
private final ReadOnlyObjectProperty<Path> pcloudLocation;
|
||||
private final BooleanBinding foundIclouddrive;
|
||||
private final BooleanBinding foundDropbox;
|
||||
private final BooleanBinding foundGdrive;
|
||||
private final BooleanBinding foundOnedrive;
|
||||
private final BooleanBinding foundMega;
|
||||
private final BooleanBinding foundPcloud;
|
||||
|
||||
@Inject
|
||||
public LocationPresets() {
|
||||
@@ -32,10 +38,14 @@ public class LocationPresets {
|
||||
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
|
||||
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
|
||||
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS));
|
||||
this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS));
|
||||
this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS));
|
||||
this.foundIclouddrive = iclouddriveLocation.isNotNull();
|
||||
this.foundDropbox = dropboxLocation.isNotNull();
|
||||
this.foundGdrive = gdriveLocation.isNotNull();
|
||||
this.foundOnedrive = onedriveLocation.isNotNull();
|
||||
this.foundMega = megaLocation.isNotNull();
|
||||
this.foundPcloud = pcloudLocation.isNotNull();
|
||||
}
|
||||
|
||||
private static Path existingWritablePath(String... candidates) {
|
||||
@@ -122,4 +132,36 @@ public class LocationPresets {
|
||||
return foundOnedrive.get();
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<Path> megaLocationProperty() {
|
||||
return megaLocation;
|
||||
}
|
||||
|
||||
public Path getMegaLocation() {
|
||||
return megaLocation.get();
|
||||
}
|
||||
|
||||
public BooleanBinding foundMegaProperty() {
|
||||
return foundMega;
|
||||
}
|
||||
|
||||
public boolean isFoundMega() {
|
||||
return foundMega.get();
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<Path> pcloudLocationProperty() {
|
||||
return pcloudLocation;
|
||||
}
|
||||
|
||||
public Path getPcloudLocation() {
|
||||
return pcloudLocation.get();
|
||||
}
|
||||
|
||||
public BooleanBinding foundPcloudProperty() {
|
||||
return foundPcloud;
|
||||
}
|
||||
|
||||
public boolean isFoundPcloud() {
|
||||
return foundPcloud.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
@@ -175,24 +176,29 @@ public class VaultService {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vault call() throws Volume.VolumeException {
|
||||
protected Vault call() throws Volume.VolumeException, LockNotCompletedException {
|
||||
vault.lock(forced);
|
||||
return vault;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduled() {
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
vault.setState(VaultState.LOCKED);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
vault.setState(VaultState.UNLOCKED);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelled() {
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
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.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
@@ -44,8 +46,9 @@ public class FxApplication extends Application {
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final Lazy<QuitComponent> quitWindow;
|
||||
private final Provider<UnlockComponent.Builder> unlockWindowBuilderProvider;
|
||||
private final Provider<LockComponent.Builder> lockWindowBuilderProvider;
|
||||
private final Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider;
|
||||
private final Provider<LockComponent.Builder> lockWorkflowBuilderProvider;
|
||||
private final ErrorComponent.Builder errorWindowBuilder;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Optional<UiAppearanceProvider> appearanceProvider;
|
||||
private final VaultService vaultService;
|
||||
@@ -55,13 +58,14 @@ public class FxApplication extends Application {
|
||||
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
|
||||
|
||||
@Inject
|
||||
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWindowBuilderProvider, Provider<LockComponent.Builder> lockWindowBuilderProvider, Lazy<QuitComponent> quitWindow, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
|
||||
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
|
||||
this.settings = settings;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.unlockWindowBuilderProvider = unlockWindowBuilderProvider;
|
||||
this.lockWindowBuilderProvider = lockWindowBuilderProvider;
|
||||
this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
|
||||
this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
|
||||
this.quitWindow = quitWindow;
|
||||
this.errorWindowBuilder = errorWindowBuilder;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.appearanceProvider = appearanceProvider;
|
||||
this.vaultService = vaultService;
|
||||
@@ -113,15 +117,23 @@ public class FxApplication extends Application {
|
||||
|
||||
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
unlockWindowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
|
||||
LOG.debug("Showing UnlockWindow for {}", vault.getDisplayName());
|
||||
if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
|
||||
unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
|
||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
lockWindowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
|
||||
lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
@@ -24,11 +25,13 @@ import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@Singleton
|
||||
public class AppLifecycleListener {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
|
||||
public static final Set<VaultState> STATES_ALLOWING_TERMINATION = EnumSet.of(VaultState.LOCKED, VaultState.NEEDS_MIGRATION, VaultState.MISSING, VaultState.ERROR);
|
||||
public static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
|
||||
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final CountDownLatch shutdownLatch;
|
||||
@@ -127,6 +130,8 @@ public class AppLifecycleListener {
|
||||
vault.lock(true);
|
||||
} catch (Volume.VolumeException e) {
|
||||
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
||||
} catch (LockNotCompletedException e) {
|
||||
LOG.error("Failed to lock vault " + vault.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.cryptomator.ui.lock;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.UserInteractionLock;
|
||||
@@ -35,21 +37,23 @@ public class LockWorkflow extends Task<Void> {
|
||||
private final UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock;
|
||||
private final Lazy<Scene> lockForcedScene;
|
||||
private final Lazy<Scene> lockFailedScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
|
||||
@Inject
|
||||
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene) {
|
||||
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene, ErrorComponent.Builder errorComponent) {
|
||||
this.lockWindow = lockWindow;
|
||||
this.vault = vault;
|
||||
this.forceLockDecisionLock = forceLockDecisionLock;
|
||||
this.lockForcedScene = lockForcedScene;
|
||||
this.lockFailedScene = lockFailedScene;
|
||||
this.errorComponent = errorComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void call() throws Volume.VolumeException, InterruptedException {
|
||||
protected Void call() throws Volume.VolumeException, InterruptedException, LockNotCompletedException {
|
||||
try {
|
||||
vault.lock(false);
|
||||
} catch (Volume.VolumeException e) {
|
||||
} catch (Volume.VolumeException | LockNotCompletedException e) {
|
||||
LOG.debug("Regular lock of {} failed.", vault.getDisplayName(), e);
|
||||
var decision = askUserForAction();
|
||||
switch (decision) {
|
||||
@@ -77,29 +81,29 @@ public class LockWorkflow extends Task<Void> {
|
||||
return forceLockDecisionLock.awaitInteraction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduled() {
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
LOG.info("Lock of {} succeeded.", vault.getDisplayName());
|
||||
vault.setState(VaultState.LOCKED);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
LOG.warn("Failed to lock {}.", vault.getDisplayName());
|
||||
vault.setState(VaultState.UNLOCKED);
|
||||
lockWindow.setScene(lockFailedScene.get());
|
||||
lockWindow.show();
|
||||
final var throwable = super.getException();
|
||||
LOG.warn("Lock of {} failed.", vault.getDisplayName(), throwable);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
|
||||
if (throwable instanceof Volume.VolumeException) {
|
||||
lockWindow.setScene(lockFailedScene.get());
|
||||
lockWindow.show();
|
||||
} else {
|
||||
errorComponent.cause(throwable).window(lockWindow).build().showErrorScene();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelled() {
|
||||
LOG.debug("Lock of {} canceled.", vault.getDisplayName());
|
||||
vault.setState(VaultState.UNLOCKED);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class MainWindowTitleController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void showDonationKeyPreferences() {
|
||||
application.showPreferencesWindow(SelectedPreferencesTab.DONATION_KEY);
|
||||
application.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -32,7 +32,8 @@ public class VaultDetailController implements FxController {
|
||||
this.anyVaultSelected = vault.isNotNull();
|
||||
}
|
||||
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||
// TODO deduplicate w/ VaultListCellController
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
|
||||
if (state != null) {
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
|
||||
@@ -24,7 +24,8 @@ public class VaultListCellController implements FxController {
|
||||
.map(this::getGlyphForVaultState);
|
||||
}
|
||||
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||
// TODO deduplicate w/ VaultDetailController
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
|
||||
if (state != null) {
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
|
||||
@@ -14,19 +14,13 @@ import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.ERROR;
|
||||
import static org.cryptomator.common.vaults.VaultState.LOCKED;
|
||||
import static org.cryptomator.common.vaults.VaultState.MISSING;
|
||||
import static org.cryptomator.common.vaults.VaultState.NEEDS_MIGRATION;
|
||||
import static org.cryptomator.common.vaults.VaultState.UNLOCKED;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListContextMenuController implements FxController {
|
||||
@@ -37,7 +31,7 @@ public class VaultListContextMenuController implements FxController {
|
||||
private final KeychainManager keychain;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
private final OptionalBinding<VaultState> selectedVaultState;
|
||||
private final OptionalBinding<VaultState.Value> selectedVaultState;
|
||||
private final Binding<Boolean> selectedVaultPassphraseStored;
|
||||
private final Binding<Boolean> selectedVaultRemovable;
|
||||
private final Binding<Boolean> selectedVaultUnlockable;
|
||||
@@ -57,7 +51,6 @@ public class VaultListContextMenuController implements FxController {
|
||||
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION)::contains).orElse(false);
|
||||
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
|
||||
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
|
||||
|
||||
}
|
||||
|
||||
private boolean isPasswordStored(Vault vault) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -15,26 +17,39 @@ import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.input.ContextMenuEvent;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListController implements FxController {
|
||||
|
||||
|
||||
private final Stage mainWindow;
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final ObjectProperty<Vault> selectedVault;
|
||||
private final VaultListCellFactory cellFactory;
|
||||
private final AddVaultWizardComponent.Builder addVaultWizard;
|
||||
private final BooleanBinding emptyVaultList;
|
||||
private final RemoveVaultComponent.Builder removeVaultDialogue;
|
||||
|
||||
public ListView<Vault> vaultList;
|
||||
|
||||
@Inject
|
||||
VaultListController(ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard) {
|
||||
VaultListController(@MainWindow Stage mainWindow, ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVaultDialogue) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.vaults = vaults;
|
||||
this.selectedVault = selectedVault;
|
||||
this.cellFactory = cellFactory;
|
||||
this.addVaultWizard = addVaultWizard;
|
||||
this.removeVaultDialogue = removeVaultDialogue;
|
||||
|
||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||
|
||||
@@ -59,6 +74,28 @@ public class VaultListController implements FxController {
|
||||
request.consume();
|
||||
}
|
||||
});
|
||||
vaultList.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
|
||||
if (keyEvent.getCode() == KeyCode.DELETE) {
|
||||
pressedShortcutToRemoveVault();
|
||||
keyEvent.consume();
|
||||
}
|
||||
});
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
vaultList.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
|
||||
if (keyEvent.getCode() == KeyCode.BACK_SPACE) {
|
||||
pressedShortcutToRemoveVault();
|
||||
keyEvent.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//register vault selection shortcut to the main window
|
||||
mainWindow.addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
|
||||
if (keyEvent.isShortcutDown() && keyEvent.getCode().isDigitKey()) {
|
||||
vaultList.getSelectionModel().select(Integer.parseInt(keyEvent.getText()) - 1);
|
||||
keyEvent.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deselect(MouseEvent released) {
|
||||
@@ -80,6 +117,13 @@ public class VaultListController implements FxController {
|
||||
addVaultWizard.build().showAddVaultWizard();
|
||||
}
|
||||
|
||||
private void pressedShortcutToRemoveVault() {
|
||||
final var vault = selectedVault.get();
|
||||
if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION).contains(vault.getState())) {
|
||||
removeVaultDialogue.vault(vault).build().showRemoveVault();
|
||||
}
|
||||
}
|
||||
|
||||
// Getter and Setter
|
||||
|
||||
public BooleanBinding emptyVaultListProperty() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -89,7 +90,10 @@ public class MigrationRunController implements FxController {
|
||||
if (keychain.isSupported()) {
|
||||
loadStoredPassword();
|
||||
}
|
||||
migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty()));
|
||||
|
||||
migrationButtonDisabled.bind(ObjectExpression.objectExpression(vault.stateProperty())
|
||||
.isNotEqualTo(VaultState.Value.NEEDS_MIGRATION)
|
||||
.or(passwordField.textProperty().isEmpty()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -101,7 +105,7 @@ public class MigrationRunController implements FxController {
|
||||
public void migrate() {
|
||||
LOG.info("Migrating vault {}", vault.getPath());
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
vault.stateProperty().transition(VaultState.Value.NEEDS_MIGRATION, VaultState.Value.PROCESSING);
|
||||
passwordField.setDisable(true);
|
||||
ScheduledFuture<?> progressSyncTask = scheduler.scheduleAtFixedRate(() -> {
|
||||
Platform.runLater(() -> {
|
||||
@@ -115,10 +119,10 @@ public class MigrationRunController implements FxController {
|
||||
}).onSuccess(needsAnotherMigration -> {
|
||||
if (needsAnotherMigration) {
|
||||
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayName());
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
} else {
|
||||
LOG.info("Migration of '{}' succeeded.", vault.getDisplayName());
|
||||
vault.setState(VaultState.LOCKED);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
passwordField.wipe();
|
||||
window.setScene(successScene.get());
|
||||
}
|
||||
@@ -127,20 +131,20 @@ public class MigrationRunController implements FxController {
|
||||
passwordField.setDisable(false);
|
||||
passwordField.selectAll();
|
||||
passwordField.requestFocus();
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
}).onError(FileSystemCapabilityChecker.MissingCapabilityException.class, e -> {
|
||||
LOG.error("Underlying file system not supported.", e);
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
missingCapability.set(e.getMissingCapability());
|
||||
window.setScene(capabilityErrorScene.get());
|
||||
}).onError(FileNameTooLongException.class, e -> {
|
||||
LOG.error("Migration failed because the underlying file system does not support long filenames.", e);
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
|
||||
window.setScene(impossibleScene.get());
|
||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
||||
LOG.error("Migration failed for technical reasons.", e);
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
|
||||
}).andFinally(() -> {
|
||||
passwordField.setDisable(false);
|
||||
|
||||
@@ -157,8 +157,8 @@ public class GeneralPreferencesController implements FxController {
|
||||
|
||||
|
||||
@FXML
|
||||
public void showDonationTab() {
|
||||
selectedTabProperty.set(SelectedPreferencesTab.DONATION_KEY);
|
||||
public void showContributeTab() {
|
||||
selectedTabProperty.set(SelectedPreferencesTab.CONTRIBUTE);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -26,7 +26,7 @@ public class PreferencesController implements FxController {
|
||||
public Tab generalTab;
|
||||
public Tab volumeTab;
|
||||
public Tab updatesTab;
|
||||
public Tab donationKeyTab;
|
||||
public Tab contributeTab;
|
||||
public Tab aboutTab;
|
||||
|
||||
@Inject
|
||||
@@ -52,7 +52,7 @@ public class PreferencesController implements FxController {
|
||||
return switch (selectedTab) {
|
||||
case UPDATES -> updatesTab;
|
||||
case VOLUME -> volumeTab;
|
||||
case DONATION_KEY -> donationKeyTab;
|
||||
case CONTRIBUTE -> contributeTab;
|
||||
case GENERAL -> generalTab;
|
||||
case ABOUT -> aboutTab;
|
||||
case ANY -> updateAvailable.get() ? updatesTab : generalTab;
|
||||
|
||||
@@ -77,8 +77,8 @@ abstract class PreferencesModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(DonationKeyPreferencesController.class)
|
||||
abstract FxController bindDonationKeyPreferencesController(DonationKeyPreferencesController controller);
|
||||
@FxControllerKey(SupporterCertificateController.class)
|
||||
abstract FxController bindSupporterCertificatePreferencesController(SupporterCertificateController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
|
||||
@@ -22,9 +22,9 @@ public enum SelectedPreferencesTab {
|
||||
UPDATES,
|
||||
|
||||
/**
|
||||
* Show donation key tab
|
||||
* Show contribute tab
|
||||
*/
|
||||
DONATION_KEY,
|
||||
CONTRIBUTE,
|
||||
|
||||
/**
|
||||
* Show about tab
|
||||
|
||||
@@ -14,17 +14,17 @@ import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.TextFormatter;
|
||||
|
||||
@PreferencesScoped
|
||||
public class DonationKeyPreferencesController implements FxController {
|
||||
public class SupporterCertificateController implements FxController {
|
||||
|
||||
private static final String DONATION_URI = "https://store.cryptomator.org/desktop";
|
||||
private static final String SUPPORTER_URI = "https://store.cryptomator.org/desktop";
|
||||
|
||||
private final Application application;
|
||||
private final LicenseHolder licenseHolder;
|
||||
private final Settings settings;
|
||||
public TextArea donationKeyField;
|
||||
public TextArea supporterCertificateField;
|
||||
|
||||
@Inject
|
||||
DonationKeyPreferencesController(Application application, LicenseHolder licenseHolder, Settings settings) {
|
||||
SupporterCertificateController(Application application, LicenseHolder licenseHolder, Settings settings) {
|
||||
this.application = application;
|
||||
this.licenseHolder = licenseHolder;
|
||||
this.settings = settings;
|
||||
@@ -32,9 +32,9 @@ public class DonationKeyPreferencesController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
donationKeyField.setText(licenseHolder.getLicenseKey().orElse(null));
|
||||
donationKeyField.textProperty().addListener(this::registrationKeyChanged);
|
||||
donationKeyField.setTextFormatter(new TextFormatter<>(this::checkVaultNameLength));
|
||||
supporterCertificateField.setText(licenseHolder.getLicenseKey().orElse(null));
|
||||
supporterCertificateField.textProperty().addListener(this::registrationKeyChanged);
|
||||
supporterCertificateField.setTextFormatter(new TextFormatter<>(this::checkVaultNameLength));
|
||||
}
|
||||
|
||||
private TextFormatter.Change checkVaultNameLength(TextFormatter.Change change) {
|
||||
@@ -53,8 +53,8 @@ public class DonationKeyPreferencesController implements FxController {
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void getDonationKey() {
|
||||
application.getHostServices().showDocument(DONATION_URI);
|
||||
public void getSupporterCertificate() {
|
||||
application.getHostServices().showDocument(SUPPORTER_URI);
|
||||
}
|
||||
|
||||
public LicenseHolder getLicenseHolder() {
|
||||
@@ -43,8 +43,8 @@ abstract class VaultStatisticsModule {
|
||||
var weakStage = new WeakReference<>(stage);
|
||||
vault.stateProperty().addListener(new ChangeListener<>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends VaultState> observable, VaultState oldValue, VaultState newValue) {
|
||||
if (newValue != VaultState.UNLOCKED) {
|
||||
public void changed(ObservableValue<? extends VaultState.Value> observable, VaultState.Value oldValue, VaultState.Value newValue) {
|
||||
if (newValue != VaultState.Value.UNLOCKED) {
|
||||
Stage stage = weakStage.get();
|
||||
if (stage != null) {
|
||||
stage.hide();
|
||||
|
||||
@@ -44,6 +44,9 @@ class TrayMenuController {
|
||||
|
||||
public void initTrayMenu() {
|
||||
vaults.addListener(this::vaultListChanged);
|
||||
vaults.forEach(v -> {
|
||||
v.displayNameProperty().addListener(this::vaultListChanged);
|
||||
});
|
||||
rebuildMenu();
|
||||
}
|
||||
|
||||
|
||||
@@ -73,22 +73,15 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
this.successScene = successScene;
|
||||
this.invalidMountPointScene = invalidMountPointScene;
|
||||
this.errorComponent = errorComponent;
|
||||
|
||||
setOnFailed(event -> {
|
||||
Throwable throwable = event.getSource().getException();
|
||||
if (throwable instanceof InvalidMountPointException e) {
|
||||
handleInvalidMountPoint(e);
|
||||
} else {
|
||||
handleGenericError(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean call() throws InterruptedException, IOException, VolumeException, InvalidMountPointException {
|
||||
try {
|
||||
if (attemptUnlock()) {
|
||||
handleSuccess();
|
||||
if (savePassword.get()) {
|
||||
savePasswordToSystemkeychain(); //savePassword will be wiped on method return, so it must be set here
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
cancel(false); // set Tasks state to cancelled
|
||||
@@ -131,24 +124,6 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
return passwordEntryLock.awaitInteraction();
|
||||
}
|
||||
|
||||
private void handleSuccess() {
|
||||
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
|
||||
if (savePassword.get()) {
|
||||
savePasswordToSystemkeychain();
|
||||
}
|
||||
switch (vault.getVaultSettings().actionAfterUnlock().get()) {
|
||||
case ASK -> Platform.runLater(() -> {
|
||||
window.setScene(successScene.get());
|
||||
window.show();
|
||||
});
|
||||
case REVEAL -> {
|
||||
Platform.runLater(window::close);
|
||||
vaultService.reveal(vault);
|
||||
}
|
||||
case IGNORE -> Platform.runLater(window::close);
|
||||
}
|
||||
}
|
||||
|
||||
private void savePasswordToSystemkeychain() {
|
||||
if (keychain.isSupported()) {
|
||||
try {
|
||||
@@ -173,15 +148,12 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
|
||||
}
|
||||
showInvalidMountPointScene();
|
||||
return;
|
||||
} else if (cause instanceof FileAlreadyExistsException) {
|
||||
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
|
||||
showInvalidMountPointScene();
|
||||
return;
|
||||
} else if (cause instanceof DirectoryNotEmptyException) {
|
||||
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
|
||||
showInvalidMountPointScene();
|
||||
return;
|
||||
} else {
|
||||
handleGenericError(impExc);
|
||||
}
|
||||
@@ -196,7 +168,7 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
|
||||
private void handleGenericError(Throwable e) {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
errorComponent.cause(e).window(window).build().showErrorScene();
|
||||
}
|
||||
|
||||
private void wipePassword(char[] pw) {
|
||||
@@ -205,24 +177,41 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduled() {
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
vault.setState(VaultState.UNLOCKED);
|
||||
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
|
||||
|
||||
switch (vault.getVaultSettings().actionAfterUnlock().get()) {
|
||||
case ASK -> Platform.runLater(() -> {
|
||||
window.setScene(successScene.get());
|
||||
window.show();
|
||||
});
|
||||
case REVEAL -> {
|
||||
Platform.runLater(window::close);
|
||||
vaultService.reveal(vault);
|
||||
}
|
||||
case IGNORE -> Platform.runLater(window::close);
|
||||
}
|
||||
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
vault.setState(VaultState.LOCKED);
|
||||
LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
|
||||
Throwable throwable = super.getException();
|
||||
if (throwable instanceof InvalidMountPointException e) {
|
||||
handleInvalidMountPoint(e);
|
||||
} else {
|
||||
handleGenericError(throwable);
|
||||
}
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelled() {
|
||||
vault.setState(VaultState.LOCKED);
|
||||
LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName());
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
alignment="CENTER_LEFT">
|
||||
<fx:define>
|
||||
<ToggleGroup fx:id="predefinedLocationToggler"/>
|
||||
<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES" />
|
||||
<FontAwesome5IconView fx:id="goodLocation" styleClass="glyph-icon-primary" glyph="CHECK" />
|
||||
</fx:define>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="24"/>
|
||||
@@ -33,6 +35,8 @@
|
||||
<RadioButton fx:id="dropboxRadioButton" toggleGroup="${predefinedLocationToggler}" text="Dropbox" visible="${controller.locationPresets.foundDropbox}" managed="${controller.locationPresets.foundDropbox}"/>
|
||||
<RadioButton fx:id="gdriveRadioButton" toggleGroup="${predefinedLocationToggler}" text="Google Drive" visible="${controller.locationPresets.foundGdrive}" managed="${controller.locationPresets.foundGdrive}"/>
|
||||
<RadioButton fx:id="onedriveRadioButton" toggleGroup="${predefinedLocationToggler}" text="OneDrive" visible="${controller.locationPresets.foundOnedrive}" managed="${controller.locationPresets.foundOnedrive}"/>
|
||||
<RadioButton fx:id="megaRadioButton" toggleGroup="${predefinedLocationToggler}" text="MEGA" visible="${controller.locationPresets.foundMega}" managed="${controller.locationPresets.foundMega}"/>
|
||||
<RadioButton fx:id="pcloudRadioButton" toggleGroup="${predefinedLocationToggler}" text="pCloud" visible="${controller.locationPresets.foundPcloud}" managed="${controller.locationPresets.foundPcloud}"/>
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<RadioButton fx:id="customRadioButton" toggleGroup="${predefinedLocationToggler}" text="%addvaultwizard.new.directoryPickerLabel"/>
|
||||
<Button contentDisplay="LEFT" text="%addvaultwizard.new.directoryPickerButton" onAction="#chooseCustomVaultPath" disable="${controller.usePresetPath}">
|
||||
@@ -47,12 +51,8 @@
|
||||
|
||||
<VBox spacing="6">
|
||||
<Label text="%addvaultwizard.new.locationLabel" labelFor="$locationTextField"/>
|
||||
<TextField fx:id="locationTextField" promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" disable="true" HBox.hgrow="ALWAYS"/>
|
||||
<Label text="${controller.warningText}" wrapText="true" visible="${controller.showWarning}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="EXCLAMATION_TRIANGLE"/>
|
||||
</graphic>
|
||||
</Label>
|
||||
<TextField promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" editable="false" disable="${!controller.anyRadioButtonSelected}" HBox.hgrow="ALWAYS"/>
|
||||
<Label fx:id="vaultPathStatus" styleClass="label-muted" alignment="CENTER_RIGHT" wrapText="true" visible="${controller.anyRadioButtonSelected}" maxWidth="Infinity" graphicTextGap="6" text="${controller.statusText}" graphic="${controller.statusGraphic}" />
|
||||
</VBox>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
@@ -38,12 +38,12 @@
|
||||
<fx:include source="/fxml/preferences_updates.fxml"/>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="donationKeyTab" id="DONATION_KEY" text="%preferences.donationKey">
|
||||
<Tab fx:id="contributeTab" id="CONTRIBUTE" text="%preferences.contribute">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="HEART"/>
|
||||
</graphic>
|
||||
<content>
|
||||
<fx:include source="/fxml/preferences_donationkey.fxml"/>
|
||||
<fx:include source="/fxml/preferences_contribute.fxml"/>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="aboutTab" id="ABOUT" text="%preferences.about">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.preferences.DonationKeyPreferencesController"
|
||||
fx:controller="org.cryptomator.ui.preferences.SupporterCertificateController"
|
||||
spacing="18">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
@@ -24,7 +24,7 @@
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CROWN" glyphSize="24"/>
|
||||
</StackPane>
|
||||
<FormattedLabel format="%preferences.donationKey.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
|
||||
<FormattedLabel format="%preferences.contribute.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
|
||||
</HBox>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${!controller.licenseHolder.validLicense}">
|
||||
@@ -33,8 +33,8 @@
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="HAND_HOLDING_HEART" glyphSize="24"/>
|
||||
</StackPane>
|
||||
<VBox HBox.hgrow="ALWAYS" spacing="6">
|
||||
<Label text="%preferences.donationKey.noDonationKey" wrapText="true" VBox.vgrow="ALWAYS"/>
|
||||
<Hyperlink text="%preferences.donationKey.getDonationKey" onAction="#getDonationKey" contentDisplay="LEFT">
|
||||
<Label text="%preferences.contribute.noCertificate" wrapText="true" VBox.vgrow="ALWAYS"/>
|
||||
<Hyperlink text="%preferences.contribute.getCertificate" onAction="#getSupporterCertificate" contentDisplay="LEFT">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="LINK"/>
|
||||
</graphic>
|
||||
@@ -43,6 +43,6 @@
|
||||
</HBox>
|
||||
</StackPane>
|
||||
|
||||
<TextArea fx:id="donationKeyField" wrapText="true" VBox.vgrow="ALWAYS" prefRowCount="6"/>
|
||||
<TextArea fx:id="supporterCertificateField" promptText="%preferences.contribute.promptText" wrapText="true" VBox.vgrow="ALWAYS" prefRowCount="6"/>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -23,7 +23,7 @@
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<Label text="%preferences.general.theme"/>
|
||||
<ChoiceBox fx:id="themeChoiceBox" disable="${!controller.licenseHolder.validLicense}"/>
|
||||
<Hyperlink styleClass="hyperlink-underline,hyperlink-muted" text="%preferences.general.unlockThemes" onAction="#showDonationTab" visible="${!controller.licenseHolder.validLicense}" managed="${!controller.licenseHolder.validLicense}"/>
|
||||
<Hyperlink styleClass="hyperlink-underline,hyperlink-muted" text="%preferences.general.unlockThemes" onAction="#showContributeTab" visible="${!controller.licenseHolder.validLicense}" managed="${!controller.licenseHolder.validLicense}"/>
|
||||
</HBox>
|
||||
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
|
||||
@@ -45,8 +45,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Custom Location
|
||||
addvaultwizard.new.directoryPickerButton=Choose…
|
||||
addvaultwizard.new.directoryPickerTitle=Select Directory
|
||||
addvaultwizard.new.fileAlreadyExists=Vault can not be created at this path because some object already exists.
|
||||
addvaultwizard.new.locationDoesNotExist=Vault can not be created at this path because at least one path component does not exist.
|
||||
addvaultwizard.new.fileAlreadyExists=A file or directory with the vault name already exists
|
||||
addvaultwizard.new.locationDoesNotExist=A directory in the specified path does not exist or cannot be accessed
|
||||
addvaultwizard.new.locationIsNotWritable=No write access at the specified path
|
||||
addvaultwizard.new.locationIsOk=Suitable location for your vault
|
||||
addvaultwizard.new.invalidName=Invalid vault name. Please consider a regular directory name.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Create Vault
|
||||
@@ -175,11 +177,14 @@ preferences.updates.currentVersion=Current Version: %s
|
||||
preferences.updates.autoUpdateCheck=Check for updates automatically
|
||||
preferences.updates.checkNowBtn=Check Now
|
||||
preferences.updates.updateAvailable=Update to version %s available.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donation
|
||||
preferences.donationKey.registeredFor=Registered for %s
|
||||
preferences.donationKey.noDonationKey=No valid donation key found. It's like a license key but for awesome people using free software. ;-)
|
||||
preferences.donationKey.getDonationKey=Get a donation key
|
||||
## Contribution
|
||||
preferences.contribute=Support Us
|
||||
preferences.contribute.registeredFor=Supporter certificate registered for %s
|
||||
preferences.contribute.noCertificate=Support Cryptomator and receive a supporter certificate. It's like a license key but for awesome people using free software. ;-)
|
||||
preferences.contribute.getCertificate=Don't have one already? Learn how you can obtain it.
|
||||
preferences.contribute.promptText=Paste supporter certificate code here
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=About
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=موقع مخصص
|
||||
addvaultwizard.new.directoryPickerButton=اختر…
|
||||
addvaultwizard.new.directoryPickerTitle=اختر القاموس
|
||||
addvaultwizard.new.fileAlreadyExists=لا يمكن إنشاء مخزن على هذا المسار لأن بعض الملفات موجودة مسبقا.
|
||||
addvaultwizard.new.locationDoesNotExist=لا يمكن إنشاء المخزن على هذا المسار لأن جزء من هذا المسار غير موجود.
|
||||
addvaultwizard.new.invalidName=اسم المخزن غير صالح. يرجى النظر في اسم الدليل المعتاد.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=انشئ حافظة
|
||||
@@ -167,11 +165,9 @@ preferences.updates.currentVersion=الإصدار الحالي: %s
|
||||
preferences.updates.autoUpdateCheck=تحقق من التحديثات اوتوماتيكيا
|
||||
preferences.updates.checkNowBtn=تحقق الان
|
||||
preferences.updates.updateAvailable=التحديث إلى الإصدار %s متاح.
|
||||
## Donation Key
|
||||
preferences.donationKey=التبرعات
|
||||
preferences.donationKey.registeredFor=مسجل لـ %s
|
||||
preferences.donationKey.noDonationKey=لم يتم العثور على مفتاح تبرع صحيح. إنه مثل مفتاح الترخيص إلا أنه للأشخاص الرائعين الذين يستخدمون البرمجيات المجانية؛-)
|
||||
preferences.donationKey.getDonationKey=الحصول على مفتاح تبرع
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=حول البرنامج
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Prilagođena lokacija
|
||||
addvaultwizard.new.directoryPickerButton=Odaberi…
|
||||
addvaultwizard.new.directoryPickerTitle=Izaberi folder
|
||||
addvaultwizard.new.fileAlreadyExists=Sef ne može biti kreiran na odabranoj lokaciji jer neki objekti tu već postoje.
|
||||
addvaultwizard.new.locationDoesNotExist=Sef ne može biti kreiran na odabranoj lokaciji jer najmanje jedna komponenta lokacije ne postoji.
|
||||
addvaultwizard.new.invalidName=Naziv sefa neispravan. Molimo razmislite o drugom nazivu.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Kreiraj novi sef
|
||||
@@ -95,8 +93,10 @@ forgetPassword.confirmBtn=Zaboravili ste šifru
|
||||
# Unlock
|
||||
unlock.title=Otključaj sef
|
||||
unlock.passwordPrompt=Unesite lozinku za "%s":
|
||||
unlock.savePassword=Zapamti šifru
|
||||
unlock.unlockBtn=Otključaj
|
||||
## Success
|
||||
unlock.success.message=Uspješno ste otključali "%s"! Vaš sef je sada dostupan putem svog virtualnog diska.
|
||||
unlock.success.rememberChoice=Zapamtite izbor, ne pokazujte ovo ponovo
|
||||
unlock.success.revealBtn=Otkrij pogon
|
||||
## Failure
|
||||
@@ -172,11 +172,9 @@ preferences.updates.currentVersion=Trenutna verzija: %s
|
||||
preferences.updates.autoUpdateCheck=Automatski provjeri ima li ažuriranja
|
||||
preferences.updates.checkNowBtn=Provjeri sada
|
||||
preferences.updates.updateAvailable=Dostupno ažuriranje na verziju %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donacija
|
||||
preferences.donationKey.registeredFor=Registrovan za %s
|
||||
preferences.donationKey.noDonationKey=Nije pronađen važeći ključ za donaciju. To je poput licence, ali za sjajne ljude koji koriste besplatni softver. ;-)
|
||||
preferences.donationKey.getDonationKey=Nabavite ključ za donaciju
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=O programu
|
||||
|
||||
@@ -221,9 +219,11 @@ main.dropZone.dropVault=Dodajte ovaj sef
|
||||
main.dropZone.unknownDragboardContent=Ako želite dodati sef, povucite ga u ovaj prozor
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Kliknite ovdje da dodate sef
|
||||
main.vaultlist.contextMenu.remove=Ukloni…
|
||||
main.vaultlist.contextMenu.lock=Zaključaj
|
||||
main.vaultlist.contextMenu.unlock=Otključaj…
|
||||
main.vaultlist.contextMenu.unlockNow=Otključaj sada
|
||||
main.vaultlist.contextMenu.vaultoptions=Pokaži opcije sefa
|
||||
main.vaultlist.contextMenu.reveal=Otkrij pogon
|
||||
main.vaultlist.addVaultBtn=Dodaj sef
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Ubicació personalitzada
|
||||
addvaultwizard.new.directoryPickerButton=Trieu…
|
||||
addvaultwizard.new.directoryPickerTitle=Seleccioneu el directori
|
||||
addvaultwizard.new.fileAlreadyExists=No es pot crear una caixa forta en aquest camí perquè ja hi ha algun objecte.
|
||||
addvaultwizard.new.locationDoesNotExist=No ha estat possible crear la caixa forta en aquest camí perquè, si més no, un component no existeix.
|
||||
addvaultwizard.new.invalidName=El nom de la caixa forta no és vàlid. Si us plau, escribiu un mom de directori amb caràcters estàndard.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Crea la caixa forta
|
||||
@@ -98,6 +96,7 @@ unlock.passwordPrompt=Introduïu la contrasenya de "%s":
|
||||
unlock.savePassword=Recorda la contrasenya
|
||||
unlock.unlockBtn=Desbloqueja
|
||||
## Success
|
||||
unlock.success.message=S'ha desblocat %s correctament! Podeu accedir a la vostra caixa forta a través de la unitat virtual.
|
||||
unlock.success.rememberChoice=Recorda l'elecció. No ho tornis a mostrar.
|
||||
unlock.success.revealBtn=Mostra la unitat
|
||||
## Failure
|
||||
@@ -173,11 +172,14 @@ preferences.updates.currentVersion=Versió actual: %s
|
||||
preferences.updates.autoUpdateCheck=Comprova automàticament si hi ha actualitzacions
|
||||
preferences.updates.checkNowBtn=Comprova-ho ara
|
||||
preferences.updates.updateAvailable=L'actualització a la versió %s es troba disponible.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donacions
|
||||
preferences.donationKey.registeredFor=Registrat per %s
|
||||
preferences.donationKey.noDonationKey=No s'ha trobar una clau de donació vàlida. És sembla a una clau de llicència però per gent meravellosa qui utilitza programari lliure. ;-)
|
||||
preferences.donationKey.getDonationKey=Obtingau una clau de donació
|
||||
## Contribution
|
||||
preferences.contribute=Doneu-nos suport
|
||||
preferences.contribute.registeredFor=Certificat de col·laborador registrat per a %s
|
||||
preferences.contribute.noCertificate=Doneu suport a Cryptomator i rebeu un certificat de col·laborador. És com una clau de llicència però per a gent meravellosa que fa servir programari lliure. ;-)
|
||||
preferences.contribute.getCertificate=Encara no en teniu cap? Sapigueu com aconseguir-ne un.
|
||||
preferences.contribute.promptText=Enganxeu aquí el certificat de col·laborador
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Quant a
|
||||
|
||||
@@ -222,9 +224,11 @@ main.dropZone.dropVault=Afegeix aquesta caixa forta
|
||||
main.dropZone.unknownDragboardContent=Si voleu afegir una caixa forta, arrossegueu-la a aquesta finestra
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Feu clic aquí per afegir una caixa forta
|
||||
main.vaultlist.contextMenu.remove=Elimina…
|
||||
main.vaultlist.contextMenu.lock=Bloqueja
|
||||
main.vaultlist.contextMenu.unlock=Desbloca…
|
||||
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
|
||||
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
|
||||
main.vaultlist.contextMenu.reveal=Mostra la unitat
|
||||
main.vaultlist.addVaultBtn=Afegir una caixa forta
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=...
|
||||
addvaultwizard.new.directoryPickerLabel=Vlastní umístění
|
||||
addvaultwizard.new.directoryPickerButton=Vybrat...
|
||||
addvaultwizard.new.directoryPickerTitle=Vyberte adresář
|
||||
addvaultwizard.new.fileAlreadyExists=Trezor nemůže být vytvořen na tomto umístění, protože stejný objekt již existuje.
|
||||
addvaultwizard.new.locationDoesNotExist=Trezor nemůže být vytvořen v tomto umístění, protože přinejmenším jedna položka z daného umístění neexistuje.
|
||||
addvaultwizard.new.fileAlreadyExists=Soubor nebo složka s tímto názvem trezoru již existuje
|
||||
addvaultwizard.new.locationDoesNotExist=Složka v zadané cestě neexistuje nebo není přístupná
|
||||
addvaultwizard.new.locationIsNotWritable=Není možné zapisovat na zadané cestě
|
||||
addvaultwizard.new.locationIsOk=Vhodné umístění pro váš trezor
|
||||
addvaultwizard.new.invalidName=Neplatné jméno trezoru. Prosím změňte jméno adresáře.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Vytvořit trezor
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Zadejte heslo pro "%s":
|
||||
unlock.savePassword=Zapamatovat heslo
|
||||
unlock.unlockBtn=Odemknout
|
||||
## Success
|
||||
unlock.success.message=Trezor "%s" byl úspěšně odemčen a nyní je dostupný jako virtuální jednotka.
|
||||
unlock.success.rememberChoice=Pamatovat si volbu, nezobrazovat to znovu
|
||||
unlock.success.revealBtn=Zobrazit jednotku
|
||||
## Failure
|
||||
@@ -109,8 +112,11 @@ unlock.error.invalidMountPoint.existing=Připojovací bod %s již existuje nebo
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.heading=Běžné uzamčení selhalo
|
||||
lock.forced.message=Uzamčení "%s" bylo zablokováno nevyřízenými operacemi nebo otevřenými soubory. Můžete vynutit uzamčení tohoto trezoru, ale přerušení I/O může mít za následek ztrátu neuložených dat.
|
||||
lock.forced.confirmBtn=Přesto uzamknout
|
||||
## Failure
|
||||
lock.fail.heading=Uzamčení trezoru selhalo.
|
||||
lock.fail.message=Trezor "%s" nelze uzamknout. Ujistěte se, že je neuložená práce uložena jinde a že jsou dokončeny důležité operace čtení/zápis. Za účelem uzavření trezoru ukončte proces Cryptomatoru.
|
||||
|
||||
# Migration
|
||||
migration.title=Upgrade trezoru
|
||||
@@ -170,11 +176,14 @@ preferences.updates.currentVersion=Aktuální verze: %s
|
||||
preferences.updates.autoUpdateCheck=Automaticky kontrolovat aktualizace
|
||||
preferences.updates.checkNowBtn=Zkontrolovat nyní
|
||||
preferences.updates.updateAvailable=Dostupná nová verze %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Příspěvek
|
||||
preferences.donationKey.registeredFor=Registrováno na: %s
|
||||
preferences.donationKey.noDonationKey=Nebyl nalezen žádný platný darovací klíč. Je to jako licenční klíč, ale pro úžasné lidi využívající software zadarmo. ;-)
|
||||
preferences.donationKey.getDonationKey=Získat darovací klíč
|
||||
## Contribution
|
||||
preferences.contribute=Podpořte nás
|
||||
preferences.contribute.registeredFor=Certifikát podporovatele byl registrován pro %s
|
||||
preferences.contribute.noCertificate=Podpořte Cryptomator a získejte certifikát podporovatele. Je to jako licenční klíč, ale pro skvělé lidi, kteří používají svobodný software. ;-)
|
||||
preferences.contribute.getCertificate=Ještě žádný nemáte? Naučte se, jak ho můžete získat.
|
||||
preferences.contribute.promptText=Sem vložte kód certifikátu podporovatele
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=O aplikaci
|
||||
|
||||
@@ -223,6 +232,7 @@ main.vaultlist.contextMenu.remove=Odstranit…
|
||||
main.vaultlist.contextMenu.lock=Zamknout
|
||||
main.vaultlist.contextMenu.unlock=Odemknout…
|
||||
main.vaultlist.contextMenu.unlockNow=Odemknout nyní
|
||||
main.vaultlist.contextMenu.vaultoptions=Zobrazit možnosti trezoru
|
||||
main.vaultlist.contextMenu.reveal=Zobrazit jednotku
|
||||
main.vaultlist.addVaultBtn=Přidat trezor
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Eigener Ort
|
||||
addvaultwizard.new.directoryPickerButton=Durchsuchen …
|
||||
addvaultwizard.new.directoryPickerTitle=Verzeichnis auswählen
|
||||
addvaultwizard.new.fileAlreadyExists=Der Tresor konnte nicht erstellt werden, da am Speicherort so eine Datei bereits existiert.
|
||||
addvaultwizard.new.locationDoesNotExist=Der Tresor kann nicht erstellt werden, da mindestens ein Speicherort nicht existiert.
|
||||
addvaultwizard.new.fileAlreadyExists=Eine Datei oder ein Ordner mit diesem Namen ist bereits vorhanden
|
||||
addvaultwizard.new.locationDoesNotExist=Der Ordner im angegebenen Pfad existiert nicht oder kann nicht geöffnet werden
|
||||
addvaultwizard.new.locationIsNotWritable=Kein Schreibzugriff auf den angegebenen Pfad
|
||||
addvaultwizard.new.locationIsOk=Geeigneter Ort für deinen Tresor
|
||||
addvaultwizard.new.invalidName=Ungültiger Tresorname. Bitte wähle einen regulären Namen, mit dem auch Verzeichnisse erstellt werden können.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Tresor erstellen
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Aktuelle Version: %s
|
||||
preferences.updates.autoUpdateCheck=Automatisch nach Updates suchen
|
||||
preferences.updates.checkNowBtn=Jetzt suchen
|
||||
preferences.updates.updateAvailable=Update auf Version %s verfügbar.
|
||||
## Donation Key
|
||||
preferences.donationKey=Spende
|
||||
preferences.donationKey.registeredFor=Registriert für %s
|
||||
preferences.donationKey.noDonationKey=Kein gültiger Spendenschlüssel gefunden. Er ist wie ein Lizenzschlüssel, aber für tolle Leute, die freie Software verwenden. ;-)
|
||||
preferences.donationKey.getDonationKey=Hol dir einen Spendenschlüssel
|
||||
## Contribution
|
||||
preferences.contribute=Unterstütze uns
|
||||
preferences.contribute.registeredFor=Supporter-Zertifikat registriert für %s
|
||||
preferences.contribute.noCertificate=Unterstütze Cryptomator und erhalte ein Supporter-Zertifikat. Es ist wie ein Lizenzschlüssel, aber für großartige Menschen, die freie Software verwenden. ;-)
|
||||
preferences.contribute.getCertificate=Du hast noch keines? Erfahre, wie du es erhalten kannst.
|
||||
preferences.contribute.promptText=Code des Supporter-Zertifikats hier einfügen
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Über
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Προσαρμοσμένη τοποθεσία
|
||||
addvaultwizard.new.directoryPickerButton=Επιλογή…
|
||||
addvaultwizard.new.directoryPickerTitle=Επιλογή φακέλου
|
||||
addvaultwizard.new.fileAlreadyExists=Το vault δεν μπορεί να δημιουργηθεί σε αυτό το μονοπάτι καθώς κάποια αντικείμενα υπάρχουν ήδη.
|
||||
addvaultwizard.new.locationDoesNotExist=Το vault δεν μπορεί να δημιουργηθεί σε αυτό το μονοπάτι καθώς τουλάχιστον ένα στοιχείο του μονοπατιού δεν υπάρχει.
|
||||
addvaultwizard.new.fileAlreadyExists=Ένα αρχείο ή φάκελος με το όνομα του vault υπάρχει ήδη
|
||||
addvaultwizard.new.locationDoesNotExist=Στην καθορισμένη διαδρομή δεν υπάρχει ή δεν μπορεί να προσπελαστεί ένας φάκελος
|
||||
addvaultwizard.new.locationIsNotWritable=Δεν υπάρχει πρόσβαση εγγραφής στην καθορισμένη διαδρομή
|
||||
addvaultwizard.new.locationIsOk=Κατάλληλη τοποθεσία για το vault σας
|
||||
addvaultwizard.new.invalidName=Λάθος όνομα vault. Παρακαλώ χρησιμοποιείστε ένα κανονικό όνομα φακέλου.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Δημιουργία Vault
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Εισάγετε τον κωδικό για "%s":
|
||||
unlock.savePassword=Απομνημόνευση κωδικού πρόσβασης
|
||||
unlock.unlockBtn=Ξεκλείδωμα
|
||||
## Success
|
||||
unlock.success.message=Ξεκλειδώθηκε "%s" επιτυχώς! Το vault σας είναι διαθέσιμο μέσω του εικονικού δίσκου του.
|
||||
unlock.success.rememberChoice=Απομνημόνευση επιλογής, μην ρωτήσεις ξανά
|
||||
unlock.success.revealBtn=Αποκάλυψη εικονικού δίσκου
|
||||
## Failure
|
||||
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=Τρέχουσα έκδοση: %s
|
||||
preferences.updates.autoUpdateCheck=Αυτόματος έλεγχος για ενημερώσεις
|
||||
preferences.updates.checkNowBtn=Έλεγχος τώρα
|
||||
preferences.updates.updateAvailable=Η ενημέρωση για την έκδοση %s είναι διαθέσιμη.
|
||||
## Donation Key
|
||||
preferences.donationKey=Δωρεά
|
||||
preferences.donationKey.registeredFor=Καταχωρημένο σε %s
|
||||
preferences.donationKey.noDonationKey=Δεν βρέθηκε ενεργό κλειδί δωρεάς. Είναι σαν κλειδί αγοράς αλλά για απίθανους ανθρώπους που χρησιμοποιούν δωρεάν λογισμικό. ;-)
|
||||
preferences.donationKey.getDonationKey=Πάρε ένα κλειδί δωρεάς
|
||||
## Contribution
|
||||
preferences.contribute=Υποστηρίξτε μας
|
||||
preferences.contribute.registeredFor=Το πιστοποιητικό υποστήριξης καταχωρήθηκε για %s
|
||||
preferences.contribute.noCertificate=Υποστηρίξτε το Cryptomator και λάβετε ένα πιστοποιητικό υποστηρικτή. Είναι σαν κλειδί άδειας χρήσης, αλλά για φοβερά άτομα που χρησιμοποιούν ελεύθερο λογισμικό. ;-)
|
||||
preferences.contribute.getCertificate=Δεν έχετε ένα ήδη; Μάθετε πώς μπορείτε να το αποκτήσετε.
|
||||
preferences.contribute.promptText=Επικολλήστε τον κωδικό πιστοποιητικού υποστηρικτή εδώ
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Σχετικά με
|
||||
|
||||
@@ -222,9 +228,11 @@ main.dropZone.dropVault=Προσθήκη vault
|
||||
main.dropZone.unknownDragboardContent=Αν θέλετε να προσθέσετε ένα vault, σύρετε το σε αυτό το παράθυρο
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Κάντε κλικ εδώ για να προσθέσετε ένα vault
|
||||
main.vaultlist.contextMenu.remove=Αφαίρεση…
|
||||
main.vaultlist.contextMenu.lock=Κλείδωμα
|
||||
main.vaultlist.contextMenu.unlock=Ξεκλείδωμα…
|
||||
main.vaultlist.contextMenu.unlockNow=Ξεκλείδωμα τώρα
|
||||
main.vaultlist.contextMenu.vaultoptions=Εμφάνιση επιλογών Vault
|
||||
main.vaultlist.contextMenu.reveal=Αποκάλυψη εικονικού δίσκου
|
||||
main.vaultlist.addVaultBtn=Προσθήκη Vault
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Ubicación personalizada
|
||||
addvaultwizard.new.directoryPickerButton=Elegir…
|
||||
addvaultwizard.new.directoryPickerTitle=Seleccionar directorio
|
||||
addvaultwizard.new.fileAlreadyExists=La bóveda no puede crearse en esta ruta porque ya existe un objeto.
|
||||
addvaultwizard.new.locationDoesNotExist=La bóveda no puede ser creada en esta ruta porque al menos un componente no existe.
|
||||
addvaultwizard.new.fileAlreadyExists=Ya existe un archivo o directorio con el nombre de la bóveda
|
||||
addvaultwizard.new.locationDoesNotExist=No existe un directorio en la ruta especificada o no se puede acceder
|
||||
addvaultwizard.new.locationIsNotWritable=Sin acceso de escritura en la ruta especificada
|
||||
addvaultwizard.new.locationIsOk=Ubicación adecuada para la bóveda
|
||||
addvaultwizard.new.invalidName=Nombre de bóveda inválido. Considere un nombre de directorio regular.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Crear bóveda
|
||||
@@ -174,11 +176,9 @@ preferences.updates.currentVersion=Versión actual: %s
|
||||
preferences.updates.autoUpdateCheck=Buscar actualizaciones automáticamente
|
||||
preferences.updates.checkNowBtn=Buscar ahora
|
||||
preferences.updates.updateAvailable=Actualización a la versión %s disponible.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donación
|
||||
preferences.donationKey.registeredFor=Registrado para %s
|
||||
preferences.donationKey.noDonationKey=No se encontró una clave de donación válida. Es como una clave de licencia pero para personas geniales que usan software libre.
|
||||
preferences.donationKey.getDonationKey=Obtener clave de donación
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Acerca de
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Emplacement personnalisé
|
||||
addvaultwizard.new.directoryPickerButton=Choisir...
|
||||
addvaultwizard.new.directoryPickerTitle=Choisissez le répertoire
|
||||
addvaultwizard.new.fileAlreadyExists=Le coffre ne peut pas être créé sur ce chemin car un objet existe déjà.
|
||||
addvaultwizard.new.locationDoesNotExist=Le coffre ne peut pas être créé à cet endroit car au moins un répertoire du chemin n'existe pas.
|
||||
addvaultwizard.new.fileAlreadyExists=Un fichier ou un répertoire avec ce nom existe déjà
|
||||
addvaultwizard.new.locationDoesNotExist=Un répertoire dans le chemin spécifié n'existe pas ou ne peut pas être accédé
|
||||
addvaultwizard.new.locationIsNotWritable=Pas d'accès en écriture au chemin spécifié
|
||||
addvaultwizard.new.locationIsOk=Lieu approprié pour votre coffre
|
||||
addvaultwizard.new.invalidName=Nom de coffre invalide. Préférez un nom de répertoire habituel.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Créer un coffre
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Version actuelle : “%s”
|
||||
preferences.updates.autoUpdateCheck=Vérifier automatiquement l’existence de mise à jour
|
||||
preferences.updates.checkNowBtn=Vérifier maintenant
|
||||
preferences.updates.updateAvailable=Mise à jour “%s” disponible.
|
||||
## Donation Key
|
||||
preferences.donationKey=Faire un Don
|
||||
preferences.donationKey.registeredFor=Licence enregistrée à %s
|
||||
preferences.donationKey.noDonationKey=Aucune clé de don valide trouvée. C'est comme une clé de licence mais pour des personnes géniales utilisant un logiciel libre ;-)
|
||||
preferences.donationKey.getDonationKey=Obtenir une clé de don
|
||||
## Contribution
|
||||
preferences.contribute=Nous soutenir
|
||||
preferences.contribute.registeredFor=Certificat de soutien enregistré pour %s
|
||||
preferences.contribute.noCertificate=Soutenez Cryptomator et recevez un certificat de soutien. C'est comme une clé de licence, mais pour des gens géniaux qui utilisent des logiciels libres. ;-)
|
||||
preferences.contribute.getCertificate=Vous n'en avez pas encore? Découvrez comment vous pouvez l'obtenir.
|
||||
preferences.contribute.promptText=Collez le code du certificat du supporter ici
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=A propos
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=अपने पसंद की जगह डालें
|
||||
addvaultwizard.new.directoryPickerButton=चुनें…
|
||||
addvaultwizard.new.directoryPickerTitle=निर्देशिका चुनें
|
||||
addvaultwizard.new.fileAlreadyExists=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कुछ वस्तु पहले से मौजूद है।
|
||||
addvaultwizard.new.locationDoesNotExist=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कम से कम एक पथ घटक मौजूद नहीं है।
|
||||
addvaultwizard.new.invalidName=अमान्य वॉल्ट नाम। कृपया एक नियमित निर्देशिका नाम पर विचार करें।
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=वॉल्ट बनाएं
|
||||
@@ -94,7 +92,9 @@ preferences.title=प्राथमिकताएं
|
||||
preferences.general=सामान्य
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
|
||||
@@ -43,8 +43,6 @@ addvaultwizard.new.locationLabel=Tárolási hely
|
||||
addvaultwizard.new.directoryPickerLabel=Egyedi hely
|
||||
addvaultwizard.new.directoryPickerButton=Választás…
|
||||
addvaultwizard.new.directoryPickerTitle=Könyvtár kiválasztása
|
||||
addvaultwizard.new.fileAlreadyExists=Nem lehet a széfet létrehozni ezen a helyen, mert egy fájl már létezik itt.
|
||||
addvaultwizard.new.locationDoesNotExist=Nem lehet a széfet létrehozni ezen a helyen, mert az útvonal legalább egy darabja nem létezik.
|
||||
addvaultwizard.new.invalidName=Érvénytelen széf elnevezés. Kérjük vegye figyelembe a szabályos könyvtárelnevezésre vonatkozó szabályokat.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Új széf létrehozása
|
||||
@@ -158,11 +156,9 @@ preferences.updates.currentVersion=Jelenlegi verzió: %s
|
||||
preferences.updates.autoUpdateCheck=Frissítések autómatikus keresése
|
||||
preferences.updates.checkNowBtn=Ellenőrzés most
|
||||
preferences.updates.updateAvailable=Frissítés a %s verzióra elérhető.
|
||||
## Donation Key
|
||||
preferences.donationKey=Adomány
|
||||
preferences.donationKey.registeredFor=Regisztrálva %s számára
|
||||
preferences.donationKey.noDonationKey=Nem található érvényes adománykulcs. Mint egy licenckulcs, csak olyan nagyszerű emberek számára, akik ingyenes szofvereket használnak. ;-)
|
||||
preferences.donationKey.getDonationKey=Adománykulcs beszerzése
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Rólunk
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Sesuaikan Lokasi
|
||||
addvaultwizard.new.directoryPickerButton=Pilih…
|
||||
addvaultwizard.new.directoryPickerTitle=Pilih Folder
|
||||
addvaultwizard.new.fileAlreadyExists=Brankas tidak dibuat disini karena beberapa item telah ada disini.
|
||||
addvaultwizard.new.locationDoesNotExist=Brankas tidak bisa dibuat disini karena setidaknya satu jalur komponen tidak ada.
|
||||
addvaultwizard.new.invalidName=Nama brankas salah. Harap pilih nama folder yang umum.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Buat Brankas
|
||||
@@ -97,7 +95,9 @@ preferences.title=Preferensi
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Posizione personalizzata
|
||||
addvaultwizard.new.directoryPickerButton=Scegli…
|
||||
addvaultwizard.new.directoryPickerTitle=Seleziona cartella
|
||||
addvaultwizard.new.fileAlreadyExists=La cassaforte non può essere creata in questo percorso perché un oggetto esiste già.
|
||||
addvaultwizard.new.locationDoesNotExist=La cassaforte non può essere creata in questo percorso perché almeno un componente di percorso non esiste.
|
||||
addvaultwizard.new.invalidName=Nome della cassaforte non valido. Si prega di considerare un nome di directory regolare.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Crea Cassaforte
|
||||
@@ -98,8 +96,9 @@ unlock.passwordPrompt=Inserisci la password per "%s":
|
||||
unlock.savePassword=Ricorda la Password
|
||||
unlock.unlockBtn=Sblocca
|
||||
## Success
|
||||
unlock.success.message="%s" sbloccato con successo! La tua cassaforte è ora accessibile tramite il suo drive virtuale.
|
||||
unlock.success.rememberChoice=Ricorda la scelta, non mostrare ancora
|
||||
unlock.success.revealBtn=Visualizza disco
|
||||
unlock.success.revealBtn=Rivela Drive
|
||||
## Failure
|
||||
unlock.error.heading=Impossibile sbloccare la cassaforte
|
||||
### Invalid Mount Point
|
||||
@@ -172,11 +171,9 @@ preferences.updates.currentVersion=Versione attuale %s
|
||||
preferences.updates.autoUpdateCheck=Controlla automaticamente la presenza di aggiornamenti
|
||||
preferences.updates.checkNowBtn=Controlla adesso
|
||||
preferences.updates.updateAvailable=Disponibile aggiornamento alla versione %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donazione
|
||||
preferences.donationKey.registeredFor=Registrato per %s
|
||||
preferences.donationKey.noDonationKey=Nessuna chiave di donazione valida. È come una chiave di licenza ma per persone fantastiche che usano software gratuito. ;-)
|
||||
preferences.donationKey.getDonationKey=Ottieni una chiave di donazione
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Informazioni
|
||||
|
||||
@@ -220,10 +217,12 @@ main.dropZone.dropVault=Aggiungi questa cassaforte
|
||||
main.dropZone.unknownDragboardContent=Se vuoi aggiungere una cassaforte, trascinala in questa finestra
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Clicca qui per aggiungere una cassaforte
|
||||
main.vaultlist.contextMenu.remove=Rimuovi…
|
||||
main.vaultlist.contextMenu.lock=Blocca
|
||||
main.vaultlist.contextMenu.unlock=Sblocca…
|
||||
main.vaultlist.contextMenu.unlockNow=Sblocca adesso
|
||||
main.vaultlist.contextMenu.reveal=Visualizza disco
|
||||
main.vaultlist.contextMenu.unlockNow=Sblocca Ora
|
||||
main.vaultlist.contextMenu.vaultoptions=Mostra Opzioni Cassaforte
|
||||
main.vaultlist.contextMenu.reveal=Rivela Drive
|
||||
main.vaultlist.addVaultBtn=Aggiungi Cassaforte
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=ユーザー設定
|
||||
addvaultwizard.new.directoryPickerButton=選択...
|
||||
addvaultwizard.new.directoryPickerTitle=ディレクトリを選択
|
||||
addvaultwizard.new.fileAlreadyExists=いくつかのオブジェクトが既に存在しているため、このパスに金庫を作成することはできません。
|
||||
addvaultwizard.new.locationDoesNotExist=このパスには階層がないため金庫を作成することができません。
|
||||
addvaultwizard.new.fileAlreadyExists=金庫名と同じ名前のファイルまたはディレクトリがすでに存在しています
|
||||
addvaultwizard.new.locationDoesNotExist=指定されたパスのディレクトリが存在しないかアクセスできません
|
||||
addvaultwizard.new.locationIsNotWritable=指定されたパスに書き込み権限がありません
|
||||
addvaultwizard.new.locationIsOk=金庫に適した場所
|
||||
addvaultwizard.new.invalidName=無効な金庫の名前です。一般的なディレクトリの名前を検討してください。
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=金庫を作成
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt="%s" のパスワードを入力してください:
|
||||
unlock.savePassword=パスワードを記憶させる
|
||||
unlock.unlockBtn=解錠
|
||||
## Success
|
||||
unlock.success.message="%s" の解錠に成功しました! 仮想ドライブから金庫にアクセス可能です。
|
||||
unlock.success.rememberChoice=選択を記憶させて、再度表示しない
|
||||
unlock.success.revealBtn=ドライブを表示
|
||||
## Failure
|
||||
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=現在のバージョン: %s
|
||||
preferences.updates.autoUpdateCheck=自動的に更新を確認する
|
||||
preferences.updates.checkNowBtn=今すぐ確認
|
||||
preferences.updates.updateAvailable=利用可能なバージョン %s に更新します。
|
||||
## Donation Key
|
||||
preferences.donationKey=寄付
|
||||
preferences.donationKey.registeredFor=%s で登録されています
|
||||
preferences.donationKey.noDonationKey=有効な Donation Key が見つかりませんでした。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-)
|
||||
preferences.donationKey.getDonationKey=Donation Key を入手する
|
||||
## Contribution
|
||||
preferences.contribute=サポートする
|
||||
preferences.contribute.registeredFor=サポーター証明書が %s に登録されました
|
||||
preferences.contribute.noCertificate=Cryptomator を支援し、サポーター証明書を受け取りましょう。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-)
|
||||
preferences.contribute.getCertificate=まだ証明書を手に入れていませんか? 詳細を確認しましょう。
|
||||
preferences.contribute.promptText=サポーター証明書をここに張り付けてください
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=詳細情報
|
||||
|
||||
@@ -222,9 +228,11 @@ main.dropZone.dropVault=この金庫を追加
|
||||
main.dropZone.unknownDragboardContent=このウィンドウにドラッグして、金庫を追加
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=ここをクリックして金庫を追加
|
||||
main.vaultlist.contextMenu.remove=削除...
|
||||
main.vaultlist.contextMenu.lock=施錠
|
||||
main.vaultlist.contextMenu.unlock=解錠...
|
||||
main.vaultlist.contextMenu.unlockNow=今すぐ解錠
|
||||
main.vaultlist.contextMenu.vaultoptions=金庫のオプションを表示
|
||||
main.vaultlist.contextMenu.reveal=ドライブを表示
|
||||
main.vaultlist.addVaultBtn=金庫を追加
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=사용자 지정 위치
|
||||
addvaultwizard.new.directoryPickerButton=선택
|
||||
addvaultwizard.new.directoryPickerTitle=디렉터리 선택
|
||||
addvaultwizard.new.fileAlreadyExists=이미 다른 객체가 존재하고 있어 해당 경로에 Vault를 생성할 수 없습니다.
|
||||
addvaultwizard.new.locationDoesNotExist=하나 이상의 경로 구성 요소가 없기 때문에 이 경로에 Vault가 생성 될 수 없습니다.
|
||||
addvaultwizard.new.fileAlreadyExists=Vault 내에 이미 존재하는 파일 또는 디렉터리 이름입니다.
|
||||
addvaultwizard.new.locationDoesNotExist=지정된 디렉터리가 존재하지 않거나 접근 할 수 없습니다.
|
||||
addvaultwizard.new.locationIsNotWritable=지정된 경로에 쓰기 권한이 없습니다.
|
||||
addvaultwizard.new.locationIsOk=Vault에 적당한 위치
|
||||
addvaultwizard.new.invalidName=유효하지 않은 Vault 이름입니다. 일반적인 디렉터리 이름으로 지정해주십시요.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Vault 생성
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt="%s"의 비밀번호를 입력하십시요.
|
||||
unlock.savePassword=비밀번호 기억
|
||||
unlock.unlockBtn=잠금해제
|
||||
## Success
|
||||
unlock.success.message="%s"이(가) 성공적으로 잠금해제되었습니다. 이제 이 Vault를 가상드라이브로 접근할 수 있습니다.
|
||||
unlock.success.rememberChoice=선택 기억함, 다시 묻지 않음
|
||||
unlock.success.revealBtn=드라이브 표시
|
||||
## Failure
|
||||
@@ -173,11 +176,9 @@ preferences.updates.currentVersion=현재 버전: %s
|
||||
preferences.updates.autoUpdateCheck=자동으로 업데이트 확인
|
||||
preferences.updates.checkNowBtn=지금 확인
|
||||
preferences.updates.updateAvailable=버전 %s의 업데이트가 가능합니다.
|
||||
## Donation Key
|
||||
preferences.donationKey=기부하기
|
||||
preferences.donationKey.registeredFor=%s (으)로 등록되어 있습니다.
|
||||
preferences.donationKey.noDonationKey=유효한 도네이션(기부) 키를 찾지 못했습니다. 라이센스 키와 비슷하나, 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
||||
preferences.donationKey.getDonationKey=도네이션(기부) 키 얻기
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=제품 정보
|
||||
|
||||
@@ -222,9 +223,11 @@ main.dropZone.dropVault=이 Vault를 추가
|
||||
main.dropZone.unknownDragboardContent=Vault를 추가하려면, 이 창에 드래그 하십시요.
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Vault를 추가하기 위해 이곳을 클릭합니다.
|
||||
main.vaultlist.contextMenu.remove=제거...
|
||||
main.vaultlist.contextMenu.lock=잠금
|
||||
main.vaultlist.contextMenu.unlock=잠금해제...
|
||||
main.vaultlist.contextMenu.unlockNow=지금 잠금해제
|
||||
main.vaultlist.contextMenu.vaultoptions=Vault 옵션 보기
|
||||
main.vaultlist.contextMenu.reveal=드라이브 표시
|
||||
main.vaultlist.addVaultBtn=Vault 추가
|
||||
## Vault Detail
|
||||
|
||||
@@ -43,7 +43,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Pielāgota atrašanās vieta
|
||||
addvaultwizard.new.directoryPickerButton=Izvēlies...
|
||||
addvaultwizard.new.directoryPickerTitle=Izvēlēties mapi
|
||||
addvaultwizard.new.fileAlreadyExists=Glabātuvi nevar izveidot šajā ceļā, jo kāds objekts jau pastāv.
|
||||
addvaultwizard.new.invalidName=Nederīgs glabātuves nosaukums. Lūdzu izvēlieties vienkāršu mapes nosaukumu.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Izveidot glabātuvi
|
||||
@@ -148,11 +147,9 @@ preferences.updates.currentVersion=Pašreizējā versija: %s
|
||||
preferences.updates.autoUpdateCheck=Automātiski pārbaudīt atjauninājumus
|
||||
preferences.updates.checkNowBtn=Pārbaudīt tagad
|
||||
preferences.updates.updateAvailable=Pieejams atjauninājums uz versiju %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Ziedojums
|
||||
preferences.donationKey.registeredFor=Reģistrēts uz %s
|
||||
preferences.donationKey.noDonationKey=Nav atrasta derīga ziedojuma atslēga. Tā ir kā licences atslēga , bet priekš satriecošiem cilvēkiem, kas izmanto bezmaksas programmatūru. ;-)
|
||||
preferences.donationKey.getDonationKey=Iegūt ziedojuma atslēgu
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Par lietotni
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Tilpasset lagringssted
|
||||
addvaultwizard.new.directoryPickerButton=Velg…
|
||||
addvaultwizard.new.directoryPickerTitle=Velg mappe
|
||||
addvaultwizard.new.fileAlreadyExists=Hvelvet kan ikke opprettes på denne søkestien fordi et objekt allerede eksisterer.
|
||||
addvaultwizard.new.locationDoesNotExist=Hvelvet kan ikke opprettes på denne søkestien fordi minst én søkestikomponent ikke finnes.
|
||||
addvaultwizard.new.invalidName=Ugyldig navn på hvelvet. Vennligst vurder et vanlig mappenavn.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Opprett hvelv
|
||||
@@ -173,11 +171,9 @@ preferences.updates.currentVersion=Gjeldende versjon: %s
|
||||
preferences.updates.autoUpdateCheck=Se etter oppdateringer automatisk
|
||||
preferences.updates.checkNowBtn=Sjekk nå
|
||||
preferences.updates.updateAvailable=Oppdatering til versjon %s er tilgjengelig.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donasjon
|
||||
preferences.donationKey.registeredFor=Registrert for %s
|
||||
preferences.donationKey.noDonationKey=Ingen gyldig donasjonsnøkkel funnet. Det er som en lisensnøkkel, men for fantastiske mennesker som bruker gratis programvare. ;-)
|
||||
preferences.donationKey.getDonationKey=Få en donasjonsnøkkel
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Om
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Andere locatie
|
||||
addvaultwizard.new.directoryPickerButton=Kies…
|
||||
addvaultwizard.new.directoryPickerTitle=Selecteer map
|
||||
addvaultwizard.new.fileAlreadyExists=Er kan op deze locatie geen kluis aangemaakt worden, omdat er een bepaald object bestaat.
|
||||
addvaultwizard.new.locationDoesNotExist=Er kan geen kluis op dit pad aangemaakt worden omdat ten minste één onderdeel van het pad niet bestaat.
|
||||
addvaultwizard.new.invalidName=Ongeldige kluisnaam. Overweeg een standaard mapnaam.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Kluis aanmaken
|
||||
@@ -172,11 +170,9 @@ preferences.updates.currentVersion=Huidige Versie: "%s"
|
||||
preferences.updates.autoUpdateCheck=Automatisch controleren op updates
|
||||
preferences.updates.checkNowBtn=Controleer nu
|
||||
preferences.updates.updateAvailable=Update naar versie "%s" beschikbaar.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donatie
|
||||
preferences.donationKey.registeredFor=Geregistreerd voor %s
|
||||
preferences.donationKey.noDonationKey=Geen geldige donatiesleutel gevonden. Het is als een licentiesleutel, maar dan voor geweldige mensen die gratis software gebruiken. ;-)
|
||||
preferences.donationKey.getDonationKey=Verkrijg een donatiesleutel
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Over
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Tilpassa lagringsstad
|
||||
addvaultwizard.new.directoryPickerButton=Vel…
|
||||
addvaultwizard.new.directoryPickerTitle=Vel mappe
|
||||
addvaultwizard.new.fileAlreadyExists=Kvelven kan ikkje opprettast på denne søkestien fordi eit objekt allereie eksisterer her.
|
||||
addvaultwizard.new.locationDoesNotExist=Kvelven kan ikkje opprettast på denne søkjestien fordi minst éin søkjestikomponent ikkje finst.
|
||||
addvaultwizard.new.invalidName=Ugyldig namn på kvelven. Ver vennleg og vurder eit vanleg mappenamn.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Opprett kvelven
|
||||
@@ -155,11 +153,9 @@ preferences.updates.currentVersion=Gjeldande versjon: %s
|
||||
preferences.updates.autoUpdateCheck=Sjå etter oppdateringar automatisk
|
||||
preferences.updates.checkNowBtn=Sjekk no
|
||||
preferences.updates.updateAvailable=Oppdatering til versjon %s er tilgjengeleg.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donasjon
|
||||
preferences.donationKey.registeredFor=Registrert for %s
|
||||
preferences.donationKey.noDonationKey=Ingen gyldig donasjonsnøkkel funne. Det er som ein lisensnøkkel, men for fantastiske menneske som bruker gratis programvare. ;-)
|
||||
preferences.donationKey.getDonationKey=Få ein donasjonsnøkkel
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Om
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=ਪਸੰਦੀਦਾ ਟਿਕਾਣਾ
|
||||
addvaultwizard.new.directoryPickerButton=…ਚੁਣੋ
|
||||
addvaultwizard.new.directoryPickerTitle=ਡਾਇਰੈਕਟਰੀ ਚੁਣੋ
|
||||
addvaultwizard.new.fileAlreadyExists=ਇਸ ਮਾਰਗ ਉੱਤੇ ਵਾਲਟ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਦਾ ਹੈ, ਕਿਉਂਕਿ ਕੁਝ ਚੀਜ਼ਾਂ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹਨ।
|
||||
addvaultwizard.new.locationDoesNotExist=ਇਸ ਮਾਰਗ ਉੱਤੇ ਵਾਲੇਟ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਦਾ, ਕਿਉਂਕਿ ਮਾਰਗ ਦਾ ਇੱਕ ਭਾਗ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।
|
||||
addvaultwizard.new.invalidName=ਵਾਲਟ ਦਾ ਨਾਂ ਗਲਤ ਹੈ। ਨਿਯਮਤ ਡਾਇਰੈਕਟਰੀ ਨਾਂ ਦੇਣ ਬਾਰੇ ਵਿਚਾਰੋ।
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=ਵਾਲਟ ਬਣਾਓ
|
||||
@@ -173,11 +171,9 @@ preferences.updates.currentVersion=ਮੌਜੂਦਾ ਵਰਜ਼ਨ: %s
|
||||
preferences.updates.autoUpdateCheck=ਅੱਪਡੇਟ ਲਈ ਆਪਣੇ-ਆਪ ਜਾਂਚ ਕਰੋ
|
||||
preferences.updates.checkNowBtn=ਹੁਣੇ ਜਾਂਚ ਕਰੋ
|
||||
preferences.updates.updateAvailable=ਉਪਲੱਬਧ %s ਵਰਜ਼ਨ ਲਈ ਅੱਪਡੇਟ ਕਰੋ।
|
||||
## Donation Key
|
||||
preferences.donationKey=ਦਾਨ ਦਿਓ
|
||||
preferences.donationKey.registeredFor=%s ਲਈ ਰਜਿਸਟਰ ਕਰੋ
|
||||
preferences.donationKey.noDonationKey=ਕੋਈ ਵਾਜਬ ਦਾਨ ਕੁੰਜੀ ਨਹੀਂ ਲੱਭੀ ਹੈ। ਇਹ ਲਸੰਸ ਕੁੰਜੀ ਵਾਂਗ ਹੀ ਹੈ, ਪਰ ਮੁਫ਼ਤ ਸਾਫਟਵੇਅਰ ਵਰਤਣ ਵਾਲੇ ਬੇਮਿਸਾਲ ਲੋਕਾਂ ਲਈ। ;-)
|
||||
preferences.donationKey.getDonationKey=ਦਾਨ ਕੁੰਜੀ ਦਿਓ
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=ਇਸ ਬਾਰੇ
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Własna lokalizacja
|
||||
addvaultwizard.new.directoryPickerButton=Wybierz…
|
||||
addvaultwizard.new.directoryPickerTitle=Wybierz katalog
|
||||
addvaultwizard.new.fileAlreadyExists=Sejf nie może być utworzony w tym miejscu, ponieważ jakiś obiekt już istnieje.
|
||||
addvaultwizard.new.locationDoesNotExist=Sejf nie może być utworzony w tej ścieżce, ponieważ co najmniej jeden katalog nie istnieje.
|
||||
addvaultwizard.new.fileAlreadyExists=Plik lub katalog z nazwą sejfu już istnieje
|
||||
addvaultwizard.new.locationDoesNotExist=Katalog w określonej ścieżce nie istnieje lub nie można uzyskać dostępu
|
||||
addvaultwizard.new.locationIsNotWritable=Brak możliwości zapisu w określonej ścieżce
|
||||
addvaultwizard.new.locationIsOk=Odpowiednia lokalizacja dla Twojego sejfu
|
||||
addvaultwizard.new.invalidName=Nieprawidłowa nazwa sejfu. Proszę spróbować prawidłową nazwę dla katalogu.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Utwórz sejf
|
||||
@@ -100,7 +102,7 @@ unlock.unlockBtn=Odblokuj
|
||||
## Success
|
||||
unlock.success.message=Odblokowano "%s" pomyślnie! Twój sejf jest teraz dostępny za pomocą dysku wirtualnego.
|
||||
unlock.success.rememberChoice=Zapamiętaj wybór, nie pokazuj tego ponownie
|
||||
unlock.success.revealBtn=Ujawnij Dysk
|
||||
unlock.success.revealBtn=Pokaż Dysk
|
||||
## Failure
|
||||
unlock.error.heading=Nie można odblokować sejfu
|
||||
### Invalid Mount Point
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Obecna wersja: %s
|
||||
preferences.updates.autoUpdateCheck=Automatycznie sprawdzaj aktualizacje
|
||||
preferences.updates.checkNowBtn=Sprawdź teraz
|
||||
preferences.updates.updateAvailable=Dostępna jest aktualizacja do wersji %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Dotacja
|
||||
preferences.donationKey.registeredFor=Zarejestrowano dla %s
|
||||
preferences.donationKey.noDonationKey=Nie znaleziono aktualnego klucza donacji. To taki klucz licencyjny, ale dla niesamowitych ludzi używających darmowego softu. ;-)
|
||||
preferences.donationKey.getDonationKey=Zdobądź klucz donacji
|
||||
## Contribution
|
||||
preferences.contribute=Wesprzyj nas
|
||||
preferences.contribute.registeredFor=Certyfikat darczyńcy zarejestrowany dla %s
|
||||
preferences.contribute.noCertificate=Wspomóż Cryptomator i otrzymaj certyfikat darczyńcy. Jest on jak klucz licencyjny, ale dla niesamowitych ludzi korzystających z darmowego oprogramowania. ;-)
|
||||
preferences.contribute.getCertificate=Nie masz jeszcze? Dowiedz się, jak możesz to uzyskać.
|
||||
preferences.contribute.promptText=Wklej tutaj kod certyfikatu darczyńcy
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=O programie
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Outro Local
|
||||
addvaultwizard.new.directoryPickerButton=Escolher…
|
||||
addvaultwizard.new.directoryPickerTitle=Selecionar diretório
|
||||
addvaultwizard.new.fileAlreadyExists=O cofre não pode ser criado neste diretório porque algum objeto já existe.
|
||||
addvaultwizard.new.locationDoesNotExist=O cofre não pode ser criado neste diretório porque pelo menos um componente de diretório não existe.
|
||||
addvaultwizard.new.invalidName=Nome de cofre inválido. Por favor considere um nome de diretório regular.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Criar Cofre
|
||||
@@ -120,11 +118,9 @@ preferences.updates.currentVersion=Versão atual: %s
|
||||
preferences.updates.autoUpdateCheck=Verificar automaticamente por atualizações
|
||||
preferences.updates.checkNowBtn=Verificar Agora
|
||||
preferences.updates.updateAvailable=Atualização para a versão %s disponível.
|
||||
## Donation Key
|
||||
preferences.donationKey=Doação
|
||||
preferences.donationKey.registeredFor=Registado para %s
|
||||
preferences.donationKey.noDonationKey=Não foi encontrada uma chave de doação válida. É como uma chave de licença, mas para pessoas incríveis que usam software livre. ;-)
|
||||
preferences.donationKey.getDonationKey=Obter chave de doação
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Sobre
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Local Personalizado
|
||||
addvaultwizard.new.directoryPickerButton=Escolher…
|
||||
addvaultwizard.new.directoryPickerTitle=Selecionar Diretório
|
||||
addvaultwizard.new.fileAlreadyExists=O cofre não pode ser criado neste local porque algum objeto já existe.
|
||||
addvaultwizard.new.locationDoesNotExist=O cofre não pode ser criado neste caminho porque pelo menos um componente do caminho não existe.
|
||||
addvaultwizard.new.fileAlreadyExists=Um arquivo ou diretório com o nome do cofre já existe
|
||||
addvaultwizard.new.locationDoesNotExist=Um diretório no caminho especificado não existe ou não pode ser acessado
|
||||
addvaultwizard.new.locationIsNotWritable=Sem acesso de escrita no caminho especificado
|
||||
addvaultwizard.new.locationIsOk=Localização adequada para o seu cofre
|
||||
addvaultwizard.new.invalidName=Nome de cofre inválido. Por favor considere usar um nome de diretório comum.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Criar Cofre
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Digite a senha para "%s":
|
||||
unlock.savePassword=Lembrar Senha
|
||||
unlock.unlockBtn=Desbloquear
|
||||
## Success
|
||||
unlock.success.message="%s" desbloqueado com êxito! Seu cofre agora está acessível na unidade virtual.
|
||||
unlock.success.rememberChoice=Lembrar opção escolhida, não mostrar isto novamente
|
||||
unlock.success.revealBtn=Revelar Volume
|
||||
## Failure
|
||||
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=Versão atual: %s
|
||||
preferences.updates.autoUpdateCheck=Buscar atualizações automaticamente
|
||||
preferences.updates.checkNowBtn=Buscar Agora
|
||||
preferences.updates.updateAvailable=Atualizar para versão %s disponível.
|
||||
## Donation Key
|
||||
preferences.donationKey=Doação
|
||||
preferences.donationKey.registeredFor=Registrado para %s
|
||||
preferences.donationKey.noDonationKey=Nenhuma chave de doação válida encontrada. É como uma chave de licença, mas para pessoas incríveis usando software gratuito. ;-)
|
||||
preferences.donationKey.getDonationKey=Obtenha uma chave de doação
|
||||
## Contribution
|
||||
preferences.contribute=Nos Apoie
|
||||
preferences.contribute.registeredFor=Certificado de apoiador registrado para %s
|
||||
preferences.contribute.noCertificate=Apoie o Cryptomator e receba um certificado de apoiador. Ele é uma chave de licença mas é para pessoas maravilhosas que usam software livre. ;-)
|
||||
preferences.contribute.getCertificate=Não tem um ainda? Saiba como obtê-lo.
|
||||
preferences.contribute.promptText=Cole o código do certificado de apoiador aqui
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Sobre
|
||||
|
||||
@@ -222,9 +228,11 @@ main.dropZone.dropVault=Adicionar este cofre
|
||||
main.dropZone.unknownDragboardContent=Se você quer adicionar um cofre, arraste-o para esta janela
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Clique aqui para adicionar um cofre
|
||||
main.vaultlist.contextMenu.remove=Remover…
|
||||
main.vaultlist.contextMenu.lock=Bloquear
|
||||
main.vaultlist.contextMenu.unlock=Desbloquear…
|
||||
main.vaultlist.contextMenu.unlockNow=Desbloquear Agora
|
||||
main.vaultlist.contextMenu.vaultoptions=Exibir Opções de Cofre
|
||||
main.vaultlist.contextMenu.reveal=Revelar Volume
|
||||
main.vaultlist.addVaultBtn=Adicionar Cofre
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Своё место
|
||||
addvaultwizard.new.directoryPickerButton=Выбрать…
|
||||
addvaultwizard.new.directoryPickerTitle=Выберите папку
|
||||
addvaultwizard.new.fileAlreadyExists=Хранилище не может быть создано по этому пути, потому что некоторые объекты уже существуют.
|
||||
addvaultwizard.new.locationDoesNotExist=Хранилище не может быть создано по этому пути, потому что по крайней мере один компонент пути не существует.
|
||||
addvaultwizard.new.fileAlreadyExists=Файл или папка с именем хранилища уже существует
|
||||
addvaultwizard.new.locationDoesNotExist=Папка с указанным адресом не существует, или недоступна
|
||||
addvaultwizard.new.locationIsNotWritable=Указанный адрес только для чтения
|
||||
addvaultwizard.new.locationIsOk=Подходящее расположение для вашего хранилища
|
||||
addvaultwizard.new.invalidName=Неверное имя хранилища. Укажите корректное имя папки.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Создать хранилище
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Текущая версия: %s
|
||||
preferences.updates.autoUpdateCheck=Автоматически проверять наличие обновлений
|
||||
preferences.updates.checkNowBtn=Проверить
|
||||
preferences.updates.updateAvailable=Доступно обновление до версии %s.
|
||||
## Donation Key
|
||||
preferences.donationKey=Пожертвование
|
||||
preferences.donationKey.registeredFor=Зарегистрировано: %s
|
||||
preferences.donationKey.noDonationKey=Не найден ключ пожертвования. Этот ключ похож на лицензионный, но для тех, кто использует бесплатное ПО. ;-)
|
||||
preferences.donationKey.getDonationKey=Получить ключ пожертвования
|
||||
## Contribution
|
||||
preferences.contribute=Поддержите нас
|
||||
preferences.contribute.registeredFor=Сертификат мецената зарегистрирован на имя %s
|
||||
preferences.contribute.noCertificate=Поддержите Cryptomator и получите сертификат мецената. Он действует как лицензионный ключ, но для тех, кто пользуется бесплатным ПО. ;-)
|
||||
preferences.contribute.getCertificate=Ещё нет сертификата? Узнайте, как его получить.
|
||||
preferences.contribute.promptText=Вставьте сюда код сертификата мецената
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=О программе
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ addvaultwizard.new.locationLabel=Umiestnenie úložiska
|
||||
addvaultwizard.new.directoryPickerLabel=Vlastné umiestnenie
|
||||
addvaultwizard.new.directoryPickerButton=Vybrať…
|
||||
addvaultwizard.new.directoryPickerTitle=Vybrať adresár
|
||||
addvaultwizard.new.fileAlreadyExists=Na tejto ceste nie je možné vytvoriť trezor, pretože niektorý objekt už existuje.
|
||||
addvaultwizard.new.locationDoesNotExist=Peňaženka sa nedá vytvoriť na danej ceste pretože ani jedna cesta neexistuje.
|
||||
addvaultwizard.new.invalidName=Neplatný názov trezoru. Zvážte bežný názov adresára.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Vytvoriť trezor
|
||||
@@ -96,6 +94,8 @@ unlock.passwordPrompt=Zadajte heslo pre "%s":
|
||||
unlock.savePassword=Odomknúť.uložiťHeslo
|
||||
unlock.unlockBtn=Odomknúť
|
||||
## Success
|
||||
unlock.success.message=Odomknutie "%s" úspešné! Vaša peňaženka je už prístupná cez jej virtuálny disk.
|
||||
unlock.success.revealBtn=Odkry disk
|
||||
## Failure
|
||||
unlock.error.heading=Nie je možné odomknúť účet
|
||||
### Invalid Mount Point
|
||||
@@ -131,7 +131,14 @@ preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeyc
|
||||
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows ochrana dát
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
preferences.contribute=Podporte nás
|
||||
preferences.contribute.registeredFor=Certifikát podporovateľa registrovaný na %s
|
||||
preferences.contribute.noCertificate=Podpor Cryptomator a získaj certifikát podporovateľa. Je to ako licenčný klúč ale dobrí ľudia používajú voľný software. ;-)
|
||||
preferences.contribute.getCertificate=Ešte ho nemáš? Sleduj ako ho vieš získať.
|
||||
preferences.contribute.promptText=Vlož sem kód certifikátu podporovateľa
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
@@ -170,9 +177,12 @@ main.preferencesBtn.tooltip=Predvoľby
|
||||
main.debugModeEnabled.tooltip=Debagovací mód je aktivovaný
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.remove=Odstráň…
|
||||
main.vaultlist.contextMenu.lock=Uzamknúť
|
||||
main.vaultlist.contextMenu.unlock=Odomknúť…
|
||||
main.vaultlist.contextMenu.unlockNow=Odomknúť teraz
|
||||
main.vaultlist.contextMenu.vaultoptions=Ukáž možnosti peňaženky
|
||||
main.vaultlist.contextMenu.reveal=Odkry disk
|
||||
main.vaultlist.addVaultBtn=Pridať trezor
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
@@ -181,6 +191,7 @@ main.vaultDetail.unlockBtn=Odomknúť…
|
||||
main.vaultDetail.unlockNowBtn=Odomknúť teraz
|
||||
main.vaultDetail.passwordSavedInKeychain=Heslo uložené
|
||||
### Unlocked
|
||||
main.vaultDetail.revealBtn=Odkry disk
|
||||
main.vaultDetail.lockBtn=Uzamknúť
|
||||
main.vaultDetail.bytesPerSecondRead=Čítanie:
|
||||
main.vaultDetail.bytesPerSecondWritten=Zápis:
|
||||
@@ -195,6 +206,7 @@ main.vaultDetail.missing.remove=Odstrániť zo zoznamu peňaženky…
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general.vaultName=Názov trezoru
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Odkry disk
|
||||
## Mount
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Vybrať…
|
||||
## Master Key
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Donation Key
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
|
||||
77
main/ui/src/main/resources/i18n/strings_sr_Latn.properties
Normal file
77
main/ui/src/main/resources/i18n/strings_sr_Latn.properties
Normal file
@@ -0,0 +1,77 @@
|
||||
# Locale Specific CSS files such as CJK, RTL,...
|
||||
|
||||
# Generics
|
||||
## Button
|
||||
## Error
|
||||
|
||||
# Defaults
|
||||
|
||||
# Tray Menu
|
||||
|
||||
# Add Vault Wizard
|
||||
## Welcome
|
||||
## New
|
||||
### Name
|
||||
### Location
|
||||
### Password
|
||||
### Information
|
||||
## Existing
|
||||
## Success
|
||||
|
||||
# Remove Vault
|
||||
|
||||
# Change Password
|
||||
|
||||
# Forget Password
|
||||
|
||||
# Unlock
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
## Failure
|
||||
|
||||
# Migration
|
||||
## Start
|
||||
## Run
|
||||
## Sucess
|
||||
## Missing file system capabilities
|
||||
## Impossible
|
||||
|
||||
# Preferences
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
## Read
|
||||
## Write
|
||||
|
||||
# Main Window
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
### Unlocked
|
||||
### Missing
|
||||
### Needs Migration
|
||||
|
||||
# Wrong File Alert
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
## Mount
|
||||
## Master Key
|
||||
|
||||
# Recovery Key
|
||||
|
||||
# New Password
|
||||
|
||||
# Quit
|
||||
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Anpassad plats
|
||||
addvaultwizard.new.directoryPickerButton=Välj…
|
||||
addvaultwizard.new.directoryPickerTitle=Välj katalog
|
||||
addvaultwizard.new.fileAlreadyExists=Valvet kan inte skapas på denna plats då vissa filer redan existerar.
|
||||
addvaultwizard.new.locationDoesNotExist=Valvet kan inte skapas på den här platsen då åtminstone en komponent inte finns.
|
||||
addvaultwizard.new.invalidName=Felaktigt namn på valvet. Vänligen ange ett vanligt katalognamn.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Skapa Valv
|
||||
@@ -98,6 +96,7 @@ unlock.passwordPrompt=Ange lösenord för "%s":
|
||||
unlock.savePassword=Kom ihåg lösenord
|
||||
unlock.unlockBtn=Lås upp
|
||||
## Success
|
||||
unlock.success.message="%s" upplåst! Ditt valv är nu åtkomligt från den virtuella enheten.
|
||||
unlock.success.rememberChoice=Kom ihåg valet, visa inte detta igen
|
||||
unlock.success.revealBtn=Visa enhet
|
||||
## Failure
|
||||
@@ -173,11 +172,9 @@ preferences.updates.currentVersion=Nuvarande version: %s
|
||||
preferences.updates.autoUpdateCheck=Sök efter uppdateringar automatiskt
|
||||
preferences.updates.checkNowBtn=Kontrollera nu
|
||||
preferences.updates.updateAvailable=Uppdatering till version %s finns tillgänglig.
|
||||
## Donation Key
|
||||
preferences.donationKey=Donation
|
||||
preferences.donationKey.registeredFor=Registrerad till %s
|
||||
preferences.donationKey.noDonationKey=Ingen giltig donationsnyckel funnen. Det är som en licensnyckel fast för grymma personer som kör fri mjukvara ;-)
|
||||
preferences.donationKey.getDonationKey=Skaffa en donationsnyckel
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Om
|
||||
|
||||
@@ -222,9 +219,11 @@ main.dropZone.dropVault=Lägg till detta valv
|
||||
main.dropZone.unknownDragboardContent=Om du vill lägga till ett valv, dra in det till detta fönster
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Klicka här för att lägga till valv
|
||||
main.vaultlist.contextMenu.remove=Ta bort…
|
||||
main.vaultlist.contextMenu.lock=Lås
|
||||
main.vaultlist.contextMenu.unlock=Lås upp…
|
||||
main.vaultlist.contextMenu.unlockNow=Lås upp nu
|
||||
main.vaultlist.contextMenu.vaultoptions=Visa inställningar för valv
|
||||
main.vaultlist.contextMenu.reveal=Visa enhet
|
||||
main.vaultlist.addVaultBtn=Lägg till valv
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Özel Konum
|
||||
addvaultwizard.new.directoryPickerButton=Seç…
|
||||
addvaultwizard.new.directoryPickerTitle=Dizin Seç
|
||||
addvaultwizard.new.fileAlreadyExists=Bazı nesneler varolduğu için kasa bu yolda oluşturulamıyor.
|
||||
addvaultwizard.new.locationDoesNotExist=En az bir yol bileşeni varolmadığı için kasa bu yolda oluşturulamıyor.
|
||||
addvaultwizard.new.fileAlreadyExists=Kasa adına sahip bir dosya ya da dizin zaten mevcut
|
||||
addvaultwizard.new.locationDoesNotExist=Belirtilen yoldaki bir dizin mevcut değil veya erişilemez
|
||||
addvaultwizard.new.locationIsNotWritable=Belirtilen yolda yazma erişimi yok
|
||||
addvaultwizard.new.locationIsOk=Kasanız için uygun yer
|
||||
addvaultwizard.new.invalidName=Geçersiz kasa adı. Lütfen normal bir dizin adı kullanın.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Kasa Oluştur
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Şuanki Sürüm: %s
|
||||
preferences.updates.autoUpdateCheck=Güncellemeleri otomatik kontrol et
|
||||
preferences.updates.checkNowBtn=Şimdi Kontrol Et
|
||||
preferences.updates.updateAvailable=%s sürümüne güncelleme mevcut.
|
||||
## Donation Key
|
||||
preferences.donationKey=Bağış
|
||||
preferences.donationKey.registeredFor=%s için kaydedildi
|
||||
preferences.donationKey.noDonationKey=Geçerli bir bağış anahtarı bulunamadı. Bu anahtar, lisans anahtarı gibidir ama ücretsiz uygulama kullanan muhteşem insanlar içindir ;-)
|
||||
preferences.donationKey.getDonationKey=Bir bağış anahtarı al
|
||||
## Contribution
|
||||
preferences.contribute=Bizi Destekle
|
||||
preferences.contribute.registeredFor=%s için destekçi sertifikası kaydedildi
|
||||
preferences.contribute.noCertificate=Cryptomator'u destekle ve bir destekçi sertifikası al. Bu bir lisans anahtarı gibidir ama ücretsiz program kullanan müthiş insanlar için. ;-)
|
||||
preferences.contribute.getCertificate=Herhangi bir tane yok mu? Nasıl elde edebileceğinizi öğrenin.
|
||||
preferences.contribute.promptText=Destekçi sertifika kodunu buraya yapıştırın
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Hakkında
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=自定义位置
|
||||
addvaultwizard.new.directoryPickerButton=选择...
|
||||
addvaultwizard.new.directoryPickerTitle=选择目录
|
||||
addvaultwizard.new.fileAlreadyExists=无法在此路径上创建保险库,因为某些对象已经存在。
|
||||
addvaultwizard.new.locationDoesNotExist=无法在此路径上创建保险库,因为至少有一个路径组件不存在
|
||||
addvaultwizard.new.fileAlreadyExists=已存在同名文件或文件夹
|
||||
addvaultwizard.new.locationDoesNotExist=指定路径中的目录不存在或无法访问
|
||||
addvaultwizard.new.locationIsNotWritable=没有对指定路径的写入权限
|
||||
addvaultwizard.new.locationIsOk=合适的保险库路径
|
||||
addvaultwizard.new.invalidName=无效的保险库名称,请考虑一个常规的目录名称
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=创建保险库
|
||||
@@ -98,6 +100,7 @@ unlock.passwordPrompt=输入 "%s" 的密码
|
||||
unlock.savePassword=记住密码
|
||||
unlock.unlockBtn=解锁
|
||||
## Success
|
||||
unlock.success.message=已成功解锁 "%s"! 您现在可以通过其虚拟驱动器访问它
|
||||
unlock.success.rememberChoice=记住选项且不再显示
|
||||
unlock.success.revealBtn=显示驱动器
|
||||
## Failure
|
||||
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=当前版本:%s
|
||||
preferences.updates.autoUpdateCheck=自动检查更新
|
||||
preferences.updates.checkNowBtn=立即检查
|
||||
preferences.updates.updateAvailable=可更新到版本 %s
|
||||
## Donation Key
|
||||
preferences.donationKey=捐赠
|
||||
preferences.donationKey.registeredFor=已注册给 %s
|
||||
preferences.donationKey.noDonationKey=找不到有效的捐赠密钥,它相当于一个专门给使用免费软件的帅哥美女的许可证密钥 ;-)
|
||||
preferences.donationKey.getDonationKey=获取一个捐赠密钥
|
||||
## Contribution
|
||||
preferences.contribute=支持我们
|
||||
preferences.contribute.registeredFor=已为 %s 注册支持者证书
|
||||
preferences.contribute.noCertificate=支持 Cryptomator 并获得一份支持者证书。它类似于许可证密钥,特别之处是提供给使用免费软件的牛人的 ;-)
|
||||
preferences.contribute.getCertificate=还没有该证书吗?了解您如何获取
|
||||
preferences.contribute.promptText=请在此粘贴支持者证书代码
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=关于
|
||||
|
||||
@@ -222,9 +228,11 @@ main.dropZone.dropVault=添加此保险库
|
||||
main.dropZone.unknownDragboardContent=如果您想要添加一个保险库,将其拖动到此窗口
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=点击此处添加一个保险库
|
||||
main.vaultlist.contextMenu.remove=删除…
|
||||
main.vaultlist.contextMenu.lock=锁定
|
||||
main.vaultlist.contextMenu.unlock=解锁…
|
||||
main.vaultlist.contextMenu.unlockNow=立即解锁
|
||||
main.vaultlist.contextMenu.vaultoptions=显示保险库选项
|
||||
main.vaultlist.contextMenu.reveal=显示驱动器
|
||||
main.vaultlist.addVaultBtn=添加保险库
|
||||
## Vault Detail
|
||||
|
||||
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=自訂位置
|
||||
addvaultwizard.new.directoryPickerButton=選取
|
||||
addvaultwizard.new.directoryPickerTitle=選取資料夾
|
||||
addvaultwizard.new.fileAlreadyExists=無法在這個路徑建立加密檔案庫,因為其中已有其他檔案。
|
||||
addvaultwizard.new.locationDoesNotExist=無法在這個路徑建立加密檔案庫,因為部份路徑不存在。
|
||||
addvaultwizard.new.fileAlreadyExists=存在與加密檔案庫同名的檔案或資料夾
|
||||
addvaultwizard.new.locationDoesNotExist=指定的目錄不存在或無法存取
|
||||
addvaultwizard.new.locationIsNotWritable=指定路徑沒有寫入權限
|
||||
addvaultwizard.new.locationIsOk=可放置您的加密檔案庫
|
||||
addvaultwizard.new.invalidName=無效的加密檔案庫名稱。請考慮一般資料夾會使用的名稱。
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=新建加密檔案庫
|
||||
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=目前版本:%s
|
||||
preferences.updates.autoUpdateCheck=勾選以啟用自動更新
|
||||
preferences.updates.checkNowBtn=立即檢查
|
||||
preferences.updates.updateAvailable=有版本 %s 可更新。
|
||||
## Donation Key
|
||||
preferences.donationKey=贊助
|
||||
preferences.donationKey.registeredFor=註冊給 %s
|
||||
preferences.donationKey.noDonationKey=未發現贊助金鑰。贊助金鑰就像授權金鑰,不過是給使用自由軟體的善心人士的。
|
||||
preferences.donationKey.getDonationKey=取得贊助金鑰
|
||||
## Contribution
|
||||
preferences.contribute=贊助我們
|
||||
preferences.contribute.registeredFor=贊助憑證註冊給 %s
|
||||
preferences.contribute.noCertificate=支持 Cryptomator 並取得一個贊助憑證。它就像一個授權金鑰,但是給使用自由軟體的大好人的。;-)
|
||||
preferences.contribute.getCertificate=還沒有嗎?看看您該如何取得它。
|
||||
preferences.contribute.promptText=在這裡貼上贊助憑證
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=關於
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
|
||||
GPLv2+CE:
|
||||
- Java Servlet API (javax.servlet:javax.servlet-api:4.0.1 - https://javaee.github.io/servlet-spec/)
|
||||
- javafx-base (org.openjfx:javafx-base:15 - https://openjdk.java.net/projects/openjfx/javafx-base/)
|
||||
- javafx-controls (org.openjfx:javafx-controls:15 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
|
||||
- javafx-fxml (org.openjfx:javafx-fxml:15 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
|
||||
- javafx-graphics (org.openjfx:javafx-graphics:15 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
|
||||
- javafx-base (org.openjfx:javafx-base:16 - https://openjdk.java.net/projects/openjfx/javafx-base/)
|
||||
- javafx-controls (org.openjfx:javafx-controls:16 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
|
||||
- javafx-fxml (org.openjfx:javafx-fxml:16 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
|
||||
- javafx-graphics (org.openjfx:javafx-graphics:16 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
|
||||
LGPL 2.1:
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
|
||||
- Java Native Access (net.java.dev.jna:jna:5.7.0 - https://github.com/java-native-access/jna)
|
||||
|
||||
Reference in New Issue
Block a user