Feature/custom mount point generalizing (#797)

* changing naming of individual mout path property

* adding input/output for the new custom mount points

* ui integration of custom mount point

* removing unused variable

* Improving UX

* Simplify mountPathProperty and implement its usage

* reverting renaming of Properties concerning the usage of own mount point (rescheduled for 1.5.0)

* changing displayed message when no mount path given

* fixing ui error

* applying suggestion of comment 7338fda418 (r248254180)
This commit is contained in:
Armin Schrenk
2019-01-16 15:08:00 +01:00
committed by Sebastian Stenzel
parent 078a127182
commit bbe1ef3dbc
7 changed files with 107 additions and 99 deletions

View File

@@ -5,16 +5,6 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
@@ -22,7 +12,20 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.commons.lang3.StringUtils;
import org.fxmisc.easybind.EasyBind;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;
/**
* The settings specific to a single vault.
* TODO: Change the name of individualMountPath and its derivatives to customMountPath
*/
public class VaultSettings {
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;

View File

@@ -5,14 +5,13 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import java.io.IOException;
import java.nio.file.Paths;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.nio.file.Paths;
class VaultSettingsJsonAdapter {
@@ -27,8 +26,7 @@ class VaultSettingsJsonAdapter {
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
out.name("revealAfterMount").value(value.revealAfterMount().get());
out.name("usesIndividualMountPath").value(value.usesIndividualMountPath().get());
//TODO: should this always be written? ( because it could contain metadata, which the user does not want to save!)
out.name("individualMountPath").value(value.individualMountPath().get());
out.name("individualMountPath").value(value.individualMountPath().get()); //TODO: should this always be written? ( because it could contain metadata, which the user may not want to save!)
out.endObject();
}

View File

@@ -8,13 +8,6 @@
******************************************************************************/
package org.cryptomator.ui.controllers;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import javafx.application.Application;
@@ -45,9 +38,9 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
import org.cryptomator.frontend.webdav.ServerLifecycleException;
import org.cryptomator.keychain.KeychainAccess;
import org.cryptomator.ui.model.InvalidSettingsException;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.l10n.Localization;
import org.cryptomator.ui.model.InvalidSettingsException;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.model.WindowsDriveLetters;
import org.cryptomator.ui.util.DialogBuilderUtil;
@@ -57,6 +50,13 @@ import org.fxmisc.easybind.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
public class UnlockController implements ViewController {
private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class);
@@ -115,13 +115,13 @@ public class UnlockController implements ViewController {
private ChoiceBox<Character> winDriveLetter;
@FXML
private CheckBox useOwnMountPath;
private CheckBox useCustomMountPath;
@FXML
private Label mountPathLabel;
private Label customMountPathLabel;
@FXML
private TextField mountPath;
private TextField customMountPathField;
@FXML
private ProgressIndicator progressIndicator;
@@ -150,27 +150,31 @@ public class UnlockController implements ViewController {
savePassword.setDisable(!keychainAccess.isPresent());
unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not()));
mountPathLabel.visibleProperty().bind(useOwnMountPath.selectedProperty());
mountPath.visibleProperty().bind(useOwnMountPath.selectedProperty());
mountPath.managedProperty().bind(useOwnMountPath.selectedProperty());
mountPath.textProperty().addListener(this::mountPathDidChange);
customMountPathLabel.visibleProperty().bind(useCustomMountPath.selectedProperty());
customMountPathLabel.managedProperty().bind(useCustomMountPath.selectedProperty());
customMountPathField.visibleProperty().bind(useCustomMountPath.selectedProperty());
customMountPathField.managedProperty().bind(useCustomMountPath.selectedProperty());
customMountPathField.textProperty().addListener(this::mountPathDidChange);
winDriveLetter.setConverter(new WinDriveLetterLabelConverter());
if (SystemUtils.IS_OS_WINDOWS) {
winDriveLetter.setConverter(new WinDriveLetterLabelConverter());
useOwnMountPath.setVisible(false);
useOwnMountPath.setManaged(false);
mountPathLabel.setManaged(false);
//dirty cheat
mountPath.setMouseTransparent(true);
} else {
if (!SystemUtils.IS_OS_WINDOWS) {
winDriveLetterLabel.setVisible(false);
winDriveLetterLabel.setManaged(false);
winDriveLetter.setVisible(false);
winDriveLetter.setManaged(false);
if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) {
useOwnMountPath.setVisible(false);
useOwnMountPath.setManaged(false);
mountPathLabel.setManaged(false);
}
if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) {
useCustomMountPath.setVisible(false);
useCustomMountPath.setManaged(false);
customMountPathField.setMouseTransparent(true);
} else {
useCustomMountPath.setVisible(true);
if (SystemUtils.IS_OS_WINDOWS) {
winDriveLetter.visibleProperty().bind(useCustomMountPath.selectedProperty().not());
winDriveLetter.managedProperty().bind(useCustomMountPath.selectedProperty().not());
winDriveLetterLabel.visibleProperty().bind(useCustomMountPath.selectedProperty().not());
winDriveLetterLabel.managedProperty().bind(useCustomMountPath.selectedProperty().not());
}
}
}
@@ -210,13 +214,11 @@ public class UnlockController implements ViewController {
winDriveLetter.getItems().addAll(driveLetters.getAvailableDriveLetters());
winDriveLetter.getItems().sort(new WinDriveLetterComparator());
winDriveLetter.valueProperty().addListener(driveLetterChangeListener);
chooseSelectedDriveLetter();
}
downloadsPageLink.setVisible(false);
messageText.setText(null);
mountName.setText(vault.getMountName());
if (SystemUtils.IS_OS_WINDOWS) {
chooseSelectedDriveLetter();
}
savePassword.setSelected(false);
// auto-fill pw from keychain:
if (keychainAccess.isPresent()) {
@@ -231,14 +233,15 @@ public class UnlockController implements ViewController {
VaultSettings vaultSettings = vault.getVaultSettings();
unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get());
revealAfterMount.setSelected(vaultSettings.revealAfterMount().get());
useOwnMountPath.setSelected(vaultSettings.usesIndividualMountPath().get());
if (!settings.preferredVolumeImpl().get().equals(VolumeImpl.WEBDAV)) {
useCustomMountPath.setSelected(vaultSettings.usesIndividualMountPath().get());
customMountPathField.textProperty().setValue(vaultSettings.individualMountPath().getValueSafe());
}
vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set));
vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set));
vaultSubs = vaultSubs.and(EasyBind.subscribe(useOwnMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set));
mountPath.textProperty().setValue(vaultSettings.individualMountPath().getValueSafe());
vaultSubs = vaultSubs.and(EasyBind.subscribe(useCustomMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set));
}
@@ -282,7 +285,7 @@ public class UnlockController implements ViewController {
}
private void mountPathDidChange(ObservableValue<? extends String> property, String oldValue, String newValue) {
vault.setIndividualMountPath(newValue);
vault.setCustomMountPath(newValue);
}
/**
@@ -389,6 +392,7 @@ public class UnlockController implements ViewController {
CharSequence password = passwordField.getCharacters();
Tasks.create(() -> {
messageText.setText(localization.getString("unlock.pendingMessage.unlocking"));
vault.unlock(password);
if (keychainAccess.isPresent() && savePassword.isSelected()) {
keychainAccess.get().storePassphrase(vault.getId(), password);
@@ -400,7 +404,7 @@ public class UnlockController implements ViewController {
}).onError(InvalidSettingsException.class, e -> {
messageText.setText(localization.getString("unlock.errorMessage.invalidMountPath"));
advancedOptions.setVisible(true);
mountPath.setStyle("-fx-border-color: red;");
customMountPathField.setStyle("-fx-border-color: red;");
}).onError(InvalidPassphraseException.class, e -> {
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
passwordField.selectAll();
@@ -429,7 +433,7 @@ public class UnlockController implements ViewController {
advancedOptions.setDisable(false);
progressIndicator.setVisible(false);
if (advancedOptions.isVisible()) { //dirty programming, but otherwise the focus is wrong
mountPath.requestFocus();
customMountPathField.requestFocus();
}
}).runOnce(executor);
}

View File

@@ -1,17 +1,17 @@
package org.cryptomator.ui.model;
import javax.inject.Inject;
import java.nio.file.Paths;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import com.google.common.collect.Sets;
import com.google.common.base.Strings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.dokany.Mount;
import org.cryptomator.frontend.dokany.MountFactory;
import org.cryptomator.frontend.dokany.MountFailedException;
import javax.inject.Inject;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
public class DokanyVolume implements Volume {
private static final String FS_TYPE_NAME = "Cryptomator File System";
@@ -34,29 +34,31 @@ public class DokanyVolume implements Volume {
return DokanyVolume.isSupportedStatic();
}
//TODO: Drive letter 'A' as mount point is invalid in dokany. maybe we should do already here something against it
@Override
public void mount(CryptoFileSystem fs) throws VolumeException {
char driveLetter;
if (!vaultSettings.winDriveLetter().getValueSafe().equals("")) {
driveLetter = vaultSettings.winDriveLetter().get().charAt(0);
Path mountPath = Paths.get(getMountPathString());
String mountName = vaultSettings.mountName().get();
try {
this.mount = mountFactory.mount(fs.getPath("/"), mountPath, mountName, FS_TYPE_NAME);
} catch (MountFailedException e) {
throw new VolumeException("Unable to mount Filesystem", e);
}
}
private String getMountPathString() throws VolumeException {
if (vaultSettings.usesIndividualMountPath().get()) {
return vaultSettings.individualMountPath().get();
} else if (!Strings.isNullOrEmpty(vaultSettings.winDriveLetter().get())) {
return vaultSettings.winDriveLetter().get().charAt(0) + ":\\";
} else {
//auto assign drive letter
if (!windowsDriveLetters.getAvailableDriveLetters().isEmpty()) {
//this is a temporary fix for 'A' being an invalid drive letter
Set<Character> availableLettersWithoutA = Sets.difference(windowsDriveLetters.getAvailableDriveLetters(), Set.of('A'));
driveLetter = availableLettersWithoutA.iterator().next();
// driveLetter = windowsDriveLetters.getAvailableDriveLetters().iterator().next();
return windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\";
} else {
throw new VolumeException("No free drive letter available.");
}
}
String mountName = vaultSettings.mountName().get();
try {
this.mount = mountFactory.mount(fs.getPath("/"), Paths.get(driveLetter + ":\\") , mountName, FS_TYPE_NAME);
} catch (MountFailedException e) {
throw new VolumeException("Unable to mount Filesystem", e);
}
}
@Override

View File

@@ -8,19 +8,6 @@
*******************************************************************************/
package org.cryptomator.ui.model;
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.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.inject.Provider;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Binding;
@@ -42,6 +29,18 @@ import org.fxmisc.easybind.EasyBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Provider;
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.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
@PerVault
public class Vault {
@@ -241,11 +240,11 @@ public class Vault {
return vaultSettings.mountName().get();
}
public String getIndividualMountPath() {
public String getCustomMountPath() {
return vaultSettings.individualMountPath().getValueSafe();
}
public void setIndividualMountPath(String mountPath) {
public void setCustomMountPath(String mountPath) {
vaultSettings.individualMountPath().set(mountPath);
}

View File

@@ -82,16 +82,17 @@
<!-- Row 3.4 -->
<CheckBox GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
<!-- Row 3.5 Alt1 -->
<Label GridPane.rowIndex="5" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
<ChoiceBox GridPane.rowIndex="5" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<!-- Row 3.5 Alt2 -->
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="useOwnMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
<!-- Row 3.5 -->
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="useCustomMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
<TextField GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="mountPath" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<!-- Row 3.6 Alt1 -->
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
<ChoiceBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<!-- Row 3.6 Alt2 -->
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="customMountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
<TextField GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="customMountPathField" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
</GridPane>

View File

@@ -86,9 +86,10 @@ unlock.savePassword.delete.confirmation.title=Delete Saved Password
unlock.savePassword.delete.confirmation.header=Do you really want to delete the saved password of this vault?
unlock.savePassword.delete.confirmation.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
unlock.choicebox.winDriveLetter.auto=Assign automatically
unlock.pendingMessage.unlocking=Unlocking and mounting vault...
unlock.errorMessage.wrongPassword=Wrong password
unlock.errorMessage.wrongPassword=Wrong Password
unlock.errorMessage.invalidMountPath=Individual mount path is not set.
unlock.errorMessage.invalidMountPath=Use of custom mount path selected, but no path given.
unlock.errorMessage.unlockFailed=Unlock failed. See log file for details.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.