diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java index 438999cf1..4c985a336 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java @@ -1,10 +1,13 @@ package org.cryptomator.ui.addvaultwizard; import dagger.Lazy; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; @@ -38,14 +41,16 @@ public class CreateNewVaultLocationController implements FxController { private static final Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home")); private final Stage window; - private final Lazy previousScene; - private final Lazy nextScene; + private final Lazy chooseNameScene; + private final Lazy choosePasswordScene; private final LocationPresets locationPresets; private final ObjectProperty vaultPath; - private final BooleanBinding vaultPathIsNull; private final StringProperty vaultName; private final ResourceBundle resourceBundle; + private final BooleanBinding validVaultPath; + private final BooleanBinding invalidVaultPath; private final BooleanProperty usePresetPath; + private final StringProperty warningText; private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH; public ToggleGroup predefinedLocationToggler; @@ -53,25 +58,38 @@ public class CreateNewVaultLocationController implements FxController { public RadioButton gdriveRadioButton; public RadioButton customRadioButton; - //TODO: add parameter for next window - @Inject - CreateNewVaultLocationController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy previousScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy nextScene, LocationPresets locationPresets, ObjectProperty vaultPath, StringProperty vaultName, ResourceBundle resourceBundle) { + CreateNewVaultLocationController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy choosePasswordScene, LocationPresets locationPresets, ObjectProperty vaultPath, StringProperty vaultName, ResourceBundle resourceBundle) { this.window = window; - this.previousScene = previousScene; - this.nextScene = nextScene; + this.chooseNameScene = chooseNameScene; + this.choosePasswordScene = choosePasswordScene; this.locationPresets = locationPresets; this.vaultPath = vaultPath; this.vaultName = vaultName; this.resourceBundle = resourceBundle; - this.vaultPathIsNull = vaultPath.isNull(); + this.validVaultPath = Bindings.createBooleanBinding(this::isValidVaultPath, vaultPath); + this.invalidVaultPath = validVaultPath.not(); this.usePresetPath = new SimpleBooleanProperty(); + this.warningText = new SimpleStringProperty(); + } + + private boolean isValidVaultPath() { + return vaultPath.get() != null && Files.notExists(vaultPath.get()); } @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 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 observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) { @@ -86,7 +104,7 @@ public class CreateNewVaultLocationController implements FxController { @FXML public void back() { - window.setScene(previousScene.get()); + window.setScene(chooseNameScene.get()); } @FXML @@ -96,10 +114,10 @@ public class CreateNewVaultLocationController implements FxController { assert Files.isDirectory(vaultPath.get().getParent()); Path createdDir = Files.createDirectory(vaultPath.get()); Files.delete(createdDir); // assert: dir exists and is empty - window.setScene(nextScene.get()); + window.setScene(choosePasswordScene.get()); } catch (FileAlreadyExistsException e) { LOG.warn("Can not use already existing vault path: {}", vaultPath.get()); - // TODO show specific error text "vault can not be created at this path because some object already exists" + warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists")); } catch (NoSuchFileException | DirectoryNotEmptyException e) { LOG.error("Failed to delete recently created directory.", e); // TODO show generic error text for unexpected exception @@ -131,12 +149,12 @@ public class CreateNewVaultLocationController implements FxController { return vaultPath; } - public boolean isVaultPathIsNull() { - return vaultPathIsNull.get(); + public BooleanBinding invalidVaultPathProperty() { + return invalidVaultPath; } - public BooleanBinding vaultPathIsNullProperty() { - return vaultPathIsNull; + public Boolean getInvalidVaultPath() { + return invalidVaultPath.get(); } public LocationPresets getLocationPresets() { @@ -150,4 +168,20 @@ public class CreateNewVaultLocationController implements FxController { public boolean getUsePresetPath() { return usePresetPath.get(); } + + public StringProperty warningTextProperty() { + return warningText; + } + + public String getWarningText() { + return warningText.get(); + } + + public BooleanBinding showWarningProperty() { + return warningText.isNotEmpty(); + } + + public boolean isShowWarning() { + return showWarningProperty().get(); + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java index 23d2f9d20..d4d1d2065 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java @@ -1,7 +1,14 @@ package org.cryptomator.ui.addvaultwizard; import dagger.Lazy; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.StringBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.scene.Scene; import javafx.scene.control.TextField; @@ -16,32 +23,52 @@ import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ResourceBundle; +import java.util.regex.Pattern; -/** - * TODO: Add trim() filter to vaultName - */ @AddVaultWizardScoped public class CreateNewVaultNameController implements FxController { + private static final Pattern VALID_NAME_PATTERN = Pattern.compile("[\\w -]+", Pattern.UNICODE_CHARACTER_CLASS); + public TextField textField; private final Stage window; private final Lazy welcomeScene; - private final Lazy nextScene; + private final Lazy chooseLocationScene; + private final ObjectProperty vaultPath; private final StringProperty vaultName; - private final ResourceBundle resourceBundle; + private final BooleanBinding validVaultName; + private final BooleanBinding invalidVaultName; + private final StringBinding warningText; @Inject - CreateNewVaultNameController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy nextScene, StringProperty vaultName, ResourceBundle resourceBundle) { + CreateNewVaultNameController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, ObjectProperty vaultPath, StringProperty vaultName, ResourceBundle resourceBundle) { this.window = window; this.welcomeScene = welcomeScene; - this.nextScene = nextScene; + this.chooseLocationScene = chooseLocationScene; + this.vaultPath = vaultPath; this.vaultName = vaultName; - this.resourceBundle = resourceBundle; + this.validVaultName = Bindings.createBooleanBinding(this::isValidVaultName, vaultName); + this.invalidVaultName = validVaultName.not(); + this.warningText = Bindings.when(vaultName.isNotEmpty().and(invalidVaultName)).then(resourceBundle.getString("addvaultwizard.new.invalidName")).otherwise((String) null); } @FXML public void initialize() { vaultName.bind(textField.textProperty()); + vaultName.addListener(this::vaultNameChanged); + } + + public boolean isValidVaultName() { + return vaultName.get() != null && VALID_NAME_PATTERN.matcher(vaultName.get().trim()).matches(); + } + + private void vaultNameChanged(@SuppressWarnings("unused") Observable observable) { + if (isValidVaultName()) { + if (vaultPath.get() != null) { + // update vaultPath if it is already set but the user went back to change its name: + vaultPath.set(vaultPath.get().resolveSibling(vaultName.get())); + } + } } @FXML @@ -51,41 +78,33 @@ public class CreateNewVaultNameController implements FxController { @FXML public void next() { - if (nameIsValid()) { - window.setScene(nextScene.get()); - } else { - //TODO - } - } - - /** - * Checks if {@link CreateNewVaultNameController#vaultName}is a valid directory name in the OS by creating and deleting a directory with the given name in the temporary section of the OS - * TODO: Logging - * - * @return true, if a directory with the name already exists or can be created - */ - private boolean nameIsValid() { - try { - Path tmp = Files.createTempDirectory(vaultName.get()); - Files.deleteIfExists(tmp.toAbsolutePath()); - return true; - } catch (FileAlreadyExistsException e) { - return true; - } catch (IOException e) { - return false; - } catch (IllegalArgumentException e) { - return false; - } + window.setScene(chooseLocationScene.get()); } /* Getter/Setter */ - public String getVaultName() { - return vaultName.get(); + public BooleanBinding invalidVaultNameProperty() { + return invalidVaultName; } - public StringProperty vaultNameProperty() { - return vaultName; + public boolean isInvalidVaultName() { + return invalidVaultName.get(); + } + + public StringBinding warningTextProperty() { + return warningText; + } + + public String getWarningText() { + return warningText.get(); + } + + public BooleanBinding showWarningProperty() { + return warningText.isNotEmpty(); + } + + public boolean isShowWarning() { + return showWarningProperty().get(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 890412ad4..b45499b90 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -26,9 +26,12 @@ import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.util.PasswordStrengthUtil; import org.fxmisc.easybind.EasyBind; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ResourceBundle; @@ -36,6 +39,8 @@ import java.util.ResourceBundle; @AddVaultWizardScoped public class CreateNewVaultPasswordController implements FxController { + private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class); + private final Stage window; private final Lazy previousScene; private final StringProperty vaultName; @@ -108,17 +113,24 @@ public class CreateNewVaultPasswordController implements FxController { @FXML public void finish() { VaultSettings vaultSettings = VaultSettings.withRandomId(); - vaultSettings.path().setValue(vaultPath.get().resolve(vaultName.get())); + vaultSettings.path().setValue(vaultPath.get()); Vault newVault = vaultFactory.get(vaultSettings); try { - //TODO: why is creating the directory not part of the creation process? Files.createDirectory(vaultSettings.path().get()); + } catch (FileAlreadyExistsException e) { + // TODO show specific error screen + LOG.error("", e); + } catch (IOException e) { + // TODO show generic error screen + LOG.error("", e); + } + try { newVault.create(passwordField.getCharacters()); vaults.add(newVault); window.close(); } catch (IOException e) { - e.printStackTrace(); - //TODO + // TODO show generic error screen + LOG.error("", e); } } diff --git a/main/ui/src/main/resources/fxml/addvault_new_location.fxml b/main/ui/src/main/resources/fxml/addvault_new_location.fxml index 6673a4654..2969ca23f 100644 --- a/main/ui/src/main/resources/fxml/addvault_new_location.fxml +++ b/main/ui/src/main/resources/fxml/addvault_new_location.fxml @@ -11,6 +11,8 @@ + + + + + +