From d026afec353fd2b09dc442842fd6947d38c8120a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 21 Mar 2016 16:49:30 +0100 Subject: [PATCH 1/6] adjusted snapshot version [ci skip] --- main/commons-test/pom.xml | 9 ++++++--- main/commons/pom.xml | 9 ++++++--- main/filesystem-api/pom.xml | 2 +- main/filesystem-crypto-integration-tests/pom.xml | 2 +- main/filesystem-crypto/pom.xml | 2 +- main/filesystem-inmemory/pom.xml | 2 +- main/filesystem-invariants-tests/pom.xml | 2 +- main/filesystem-nameshortening/pom.xml | 2 +- main/filesystem-nio/pom.xml | 2 +- main/filesystem-stats/pom.xml | 2 +- main/frontend-api/pom.xml | 2 +- main/frontend-webdav/pom.xml | 2 +- main/pom.xml | 2 +- main/uber-jar/pom.xml | 2 +- main/ui/pom.xml | 2 +- 15 files changed, 25 insertions(+), 19 deletions(-) diff --git a/main/commons-test/pom.xml b/main/commons-test/pom.xml index 10d63b39d..c7fe991e3 100644 --- a/main/commons-test/pom.xml +++ b/main/commons-test/pom.xml @@ -1,13 +1,16 @@ - + 4.0.0 org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT commons-test Cryptomator common test dependencies diff --git a/main/commons/pom.xml b/main/commons/pom.xml index d90f7d91c..6dbb4ebd8 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -1,13 +1,16 @@ - + 4.0.0 org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT commons Cryptomator common diff --git a/main/filesystem-api/pom.xml b/main/filesystem-api/pom.xml index 273f34492..c0b4421ce 100644 --- a/main/filesystem-api/pom.xml +++ b/main/filesystem-api/pom.xml @@ -9,7 +9,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-api Cryptomator filesystem: API diff --git a/main/filesystem-crypto-integration-tests/pom.xml b/main/filesystem-crypto-integration-tests/pom.xml index 6135e1918..a5976979c 100644 --- a/main/filesystem-crypto-integration-tests/pom.xml +++ b/main/filesystem-crypto-integration-tests/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-crypto-integration-tests Cryptomator filesystem: Encryption layer tests diff --git a/main/filesystem-crypto/pom.xml b/main/filesystem-crypto/pom.xml index e036be507..3abd9d8b8 100644 --- a/main/filesystem-crypto/pom.xml +++ b/main/filesystem-crypto/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-crypto Cryptomator filesystem: Encryption layer diff --git a/main/filesystem-inmemory/pom.xml b/main/filesystem-inmemory/pom.xml index 569cab34d..6134992dc 100644 --- a/main/filesystem-inmemory/pom.xml +++ b/main/filesystem-inmemory/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-inmemory Cryptomator filesystem: In-memory mock diff --git a/main/filesystem-invariants-tests/pom.xml b/main/filesystem-invariants-tests/pom.xml index 1ad755a3a..aa13917ee 100644 --- a/main/filesystem-invariants-tests/pom.xml +++ b/main/filesystem-invariants-tests/pom.xml @@ -9,7 +9,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-invariants-tests Cryptomator filesystem: Invariants tests diff --git a/main/filesystem-nameshortening/pom.xml b/main/filesystem-nameshortening/pom.xml index d2923791b..d5830d78d 100644 --- a/main/filesystem-nameshortening/pom.xml +++ b/main/filesystem-nameshortening/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-nameshortening Cryptomator filesystem: Name shortening layer diff --git a/main/filesystem-nio/pom.xml b/main/filesystem-nio/pom.xml index 911d17d77..f091d5fcb 100644 --- a/main/filesystem-nio/pom.xml +++ b/main/filesystem-nio/pom.xml @@ -7,7 +7,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-nio Cryptomator filesystem: NIO-based physical layer diff --git a/main/filesystem-stats/pom.xml b/main/filesystem-stats/pom.xml index 02da75324..764daf84f 100644 --- a/main/filesystem-stats/pom.xml +++ b/main/filesystem-stats/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT filesystem-stats Cryptomator filesystem: Throughput statistics diff --git a/main/frontend-api/pom.xml b/main/frontend-api/pom.xml index 9890933de..875e85f95 100644 --- a/main/frontend-api/pom.xml +++ b/main/frontend-api/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT frontend-api Cryptomator frontend: API diff --git a/main/frontend-webdav/pom.xml b/main/frontend-webdav/pom.xml index f728e838b..64de5fea5 100644 --- a/main/frontend-webdav/pom.xml +++ b/main/frontend-webdav/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT frontend-webdav Cryptomator frontend: WebDAV frontend diff --git a/main/pom.xml b/main/pom.xml index 90c97c03b..71795ebb2 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -7,7 +7,7 @@ 4.0.0 org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT pom Cryptomator diff --git a/main/uber-jar/pom.xml b/main/uber-jar/pom.xml index 4716f9e09..833e1b3ea 100644 --- a/main/uber-jar/pom.xml +++ b/main/uber-jar/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT uber-jar pom diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 41ca04205..c1dc24234 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.0.1 + 1.1.0-SNAPSHOT ui Cryptomator GUI From 86000ac4542aecdfadcee9404cf77aeb191c50c5 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 22 Mar 2016 13:04:46 +0100 Subject: [PATCH 2/6] removed test dependencies from main project --- main/commons-test/pom.xml | 14 +++++++++----- main/commons/pom.xml | 5 +++++ main/pom.xml | 8 -------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/main/commons-test/pom.xml b/main/commons-test/pom.xml index c7fe991e3..8ae85edf0 100644 --- a/main/commons-test/pom.xml +++ b/main/commons-test/pom.xml @@ -17,10 +17,19 @@ Shared utilities for tests + + org.cryptomator + commons + + junit junit + + org.mockito + mockito-core + de.bechte.junit junit-hierarchicalcontextrunner @@ -29,11 +38,6 @@ org.hamcrest hamcrest-all - - - org.cryptomator - commons - diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 6dbb4ebd8..81ef3d6ef 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -27,6 +27,11 @@ junit test + + org.mockito + mockito-core + test + de.bechte.junit junit-hierarchicalcontextrunner diff --git a/main/pom.xml b/main/pom.xml index 71795ebb2..24a7ab945 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -254,14 +254,6 @@ org.apache.logging.log4j log4j-jul - - junit - junit - - - org.mockito - mockito-core - From 221deeda2582a301eaf281ec6a4b76d1a4e4a232 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 22 Mar 2016 21:36:26 +0100 Subject: [PATCH 3/6] removed .cryptomator directory extension when creating new vaults --- .../org/cryptomator/ui/controllers/MainController.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 54f766845..5a7488c46 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -172,19 +172,12 @@ public class MainController extends AbstractFXMLViewController { @FXML private void didClickCreateNewVault(ActionEvent event) { final FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION)); final File file = fileChooser.showSaveDialog(mainWindow); if (file == null) { return; } try { - final Path vaultDir; - // enforce .cryptomator file extension: - if (!file.getName().endsWith(Vault.VAULT_FILE_EXTENSION)) { - vaultDir = file.toPath().resolveSibling(file.getName() + Vault.VAULT_FILE_EXTENSION); - } else { - vaultDir = file.toPath(); - } + final Path vaultDir = file.toPath(); if (!Files.exists(vaultDir)) { Files.createDirectory(vaultDir); } From d0dc8819f4058cbad14560db9104f72a5a96ba15 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 24 Mar 2016 14:18:24 +0100 Subject: [PATCH 4/6] No longer using TrayIcon on linux systems * Reason: TrayIcon not supported well on linux and caused problems * Renamed TrayIconUtil to ExitUtil * fixes #177 --- .../cryptomator/ui/CryptomatorComponent.java | 2 +- .../ui/{TrayIconUtil.java => ExitUtil.java} | 27 ++++++++++++++++--- .../org/cryptomator/ui/MainApplication.java | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) rename main/ui/src/main/java/org/cryptomator/ui/{TrayIconUtil.java => ExitUtil.java} (88%) diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java index fc0563b18..b32935193 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java @@ -29,5 +29,5 @@ interface CryptomatorComponent { Localization localization(); - TrayIconUtil trayIconUtil(); + ExitUtil exitUtil(); } \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/TrayIconUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java similarity index 88% rename from main/ui/src/main/java/org/cryptomator/ui/TrayIconUtil.java rename to main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index eb56fb2e2..39cb090e3 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/TrayIconUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -38,22 +38,41 @@ import javafx.application.Platform; import javafx.stage.Stage; @Singleton -class TrayIconUtil { +class ExitUtil { - private static final Logger LOG = LoggerFactory.getLogger(TrayIconUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class); private final Stage mainWindow; private final Localization localization; private final Settings settings; @Inject - public TrayIconUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) { + public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) { this.mainWindow = mainWindow; this.localization = localization; this.settings = settings; } - public void initTrayIcon(Runnable exitCommand) { + public void initExitHandler(Runnable exitCommand) { + if (SystemUtils.IS_OS_LINUX) { + initMinimizeExitHandler(exitCommand); + } else { + initTrayIconExitHandler(exitCommand); + } + } + + private void initMinimizeExitHandler(Runnable exitCommand) { + mainWindow.setOnCloseRequest(e -> { + if (Platform.isImplicitExit()) { + exitCommand.run(); + } else { + mainWindow.setIconified(true); + e.consume(); + } + }); + } + + private void initTrayIconExitHandler(Runnable exitCommand) { final TrayIcon trayIcon = createTrayIcon(exitCommand); try { SystemTray.getSystemTray().add(trayIcon); diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java index f87614344..ed01184b3 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -67,7 +67,7 @@ public class MainApplication extends Application { // show window and start observing its focus: primaryStage.show(); ActiveWindowStyleSupport.startObservingFocus(primaryStage); - comp.trayIconUtil().initTrayIcon(this::quit); + comp.exitUtil().initExitHandler(this::quit); // open files, if requested during startup: for (String arg : getParameters().getUnnamed()) { From 553cb5ee3db2460db4bebd03025fdbf087443504 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 24 Mar 2016 22:51:40 +0100 Subject: [PATCH 5/6] Migration of vault bundles ending on ".cryptomator" to normal directories. --- .../controllers/ChangePasswordController.java | 15 +- .../ui/controllers/InitializeController.java | 15 +- .../LocalizedFXMLViewController.java | 18 +++ .../ui/controllers/MacWarningsController.java | 13 +- .../ui/controllers/MainController.java | 69 ++++++---- .../ui/controllers/NotFoundController.java | 23 ++++ .../ui/controllers/SettingsController.java | 13 +- .../ui/controllers/UnlockController.java | 37 +++-- .../ui/controllers/UnlockedController.java | 15 +- .../ui/controllers/UpgradeController.java | 128 ++++++++++++++++++ .../ui/controllers/WelcomeController.java | 11 +- .../ui/controls/DirectoryListCell.java | 60 +++----- .../ui/model/UpgradeInstruction.java | 40 ++++++ .../UpgradeVersion3DropBundleExtension.java | 55 ++++++++ .../java/org/cryptomator/ui/model/Vault.java | 66 ++++++--- .../ui/model/VaultObjectMapperProvider.java | 2 +- .../ui/settings/SettingsProvider.java | 1 - main/ui/src/main/resources/fxml/notfound.fxml | 20 +++ main/ui/src/main/resources/fxml/upgrade.fxml | 27 ++++ .../main/resources/localization.properties | 9 ++ .../main/resources/localization_de.properties | 9 ++ 21 files changed, 480 insertions(+), 166 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java create mode 100644 main/ui/src/main/resources/fxml/notfound.fxml create mode 100644 main/ui/src/main/resources/fxml/upgrade.fxml diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java index 4af980def..33f7ae11f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java @@ -12,7 +12,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.URL; import java.util.Optional; -import java.util.ResourceBundle; import javax.inject.Inject; import javax.inject.Singleton; @@ -37,19 +36,18 @@ import javafx.scene.control.Hyperlink; import javafx.scene.text.Text; @Singleton -public class ChangePasswordController extends AbstractFXMLViewController { +public class ChangePasswordController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class); private final Application app; - private final Localization localization; final ObjectProperty vault = new SimpleObjectProperty<>(); private Optional listener = Optional.empty(); @Inject public ChangePasswordController(Application app, Localization localization) { + super(localization); this.app = app; - this.localization = localization; } @FXML @@ -83,11 +81,6 @@ public class ChangePasswordController extends AbstractFXMLViewController { return getClass().getResource("/fxml/change_password.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - // **************************************** // Downloads link // **************************************** @@ -143,13 +136,13 @@ public class ChangePasswordController extends AbstractFXMLViewController { private void invokeListenerLater(ChangePasswordListener listener) { Platform.runLater(() -> { - listener.didChangePassword(this); + listener.didChangePassword(); }); } @FunctionalInterface interface ChangePasswordListener { - void didChangePassword(ChangePasswordController ctrl); + void didChangePassword(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java index a037a8b69..19a1187df 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java @@ -13,7 +13,6 @@ import java.io.UncheckedIOException; import java.net.URL; import java.nio.file.FileAlreadyExistsException; import java.util.Optional; -import java.util.ResourceBundle; import javax.inject.Inject; import javax.inject.Singleton; @@ -34,17 +33,16 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; @Singleton -public class InitializeController extends AbstractFXMLViewController { +public class InitializeController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class); - private final Localization localization; final ObjectProperty vault = new SimpleObjectProperty<>(); private Optional listener = Optional.empty(); @Inject public InitializeController(Localization localization) { - this.localization = localization; + super(localization); } @FXML @@ -71,11 +69,6 @@ public class InitializeController extends AbstractFXMLViewController { return getClass().getResource("/fxml/initialize.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - // **************************************** // OK button // **************************************** @@ -111,13 +104,13 @@ public class InitializeController extends AbstractFXMLViewController { private void invokeListenerLater(InitializationListener listener) { Platform.runLater(() -> { - listener.didInitialize(this); + listener.didInitialize(); }); } @FunctionalInterface interface InitializationListener { - void didInitialize(InitializeController ctrl); + void didInitialize(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java new file mode 100644 index 000000000..9a480fb4e --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/LocalizedFXMLViewController.java @@ -0,0 +1,18 @@ +package org.cryptomator.ui.controllers; + +import org.cryptomator.ui.settings.Localization; + +abstract class LocalizedFXMLViewController extends AbstractFXMLViewController { + + protected final Localization localization; + + public LocalizedFXMLViewController(Localization localization) { + this.localization = localization; + } + + @Override + protected Localization getFxmlResourceBundle() { + return localization; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java index 706328a7a..3752a060c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MacWarningsController.java @@ -9,7 +9,6 @@ package org.cryptomator.ui.controllers; import java.net.URL; -import java.util.ResourceBundle; import java.util.stream.Collectors; import javax.inject.Inject; @@ -39,10 +38,9 @@ import javafx.scene.control.cell.CheckBoxListCell; import javafx.stage.Stage; import javafx.util.StringConverter; -public class MacWarningsController extends AbstractFXMLViewController { +public class MacWarningsController extends LocalizedFXMLViewController { private final Application application; - private final Localization localization; private final ObservableList warnings = FXCollections.observableArrayList(); private final ListChangeListener unauthenticatedResourcesChangeListener = this::unauthenticatedResourcesDidChange; private final ChangeListener stageVisibilityChangeListener = this::windowVisibilityDidChange; @@ -51,8 +49,8 @@ public class MacWarningsController extends AbstractFXMLViewController { @Inject public MacWarningsController(Application application, Localization localization) { + super(localization); this.application = application; - this.localization = localization; } @FXML @@ -85,11 +83,6 @@ public class MacWarningsController extends AbstractFXMLViewController { return getClass().getResource("/fxml/mac_warnings.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - @Override public void initStage(Stage stage) { super.initStage(stage); @@ -130,7 +123,7 @@ public class MacWarningsController extends AbstractFXMLViewController { private void windowVisibilityDidChange(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (Boolean.TRUE.equals(newValue)) { - stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().getName())); + stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().name().getValue())); warnings.addAll(vault.get().getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList())); vault.get().getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener); } else { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 5a7488c46..5c262f6de 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -16,7 +16,6 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.ResourceBundle; import javax.inject.Inject; import javax.inject.Named; @@ -57,15 +56,16 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; @Singleton -public class MainController extends AbstractFXMLViewController { +public class MainController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(MainController.class); private final Stage mainWindow; - private final Localization localization; private final VaultFactory vaultFactoy; private final Lazy welcomeController; private final Lazy initializeController; + private final Lazy notFoundController; + private final Lazy upgradeController; private final Lazy unlockController; private final Provider unlockedControllerProvider; private final Lazy changePasswordController; @@ -73,20 +73,22 @@ public class MainController extends AbstractFXMLViewController { private final ObjectProperty activeController = new SimpleObjectProperty<>(); private final ObservableList vaults; private final ObjectProperty selectedVault = new SimpleObjectProperty<>(); - private final MonadicBinding isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty); + private final MonadicBinding isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty);; private final Binding canEditSelectedVault = EasyBind.combine(selectedVault.isNull(), isSelectedVaultUnlocked.orElse(false), Boolean::logicalOr); private final BooleanBinding isShowingSettings; private final Map unlockedVaults = new HashMap<>(); @Inject public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy welcomeController, - Lazy initializeController, Lazy unlockController, Provider unlockedControllerProvider, Lazy changePasswordController, - Lazy settingsController) { + Lazy initializeController, Lazy notFoundController, Lazy upgradeController, Lazy unlockController, + Provider unlockedControllerProvider, Lazy changePasswordController, Lazy settingsController) { + super(localization); this.mainWindow = mainWindow; - this.localization = localization; this.vaultFactoy = vaultFactoy; this.welcomeController = welcomeController; this.initializeController = initializeController; + this.notFoundController = notFoundController; + this.upgradeController = upgradeController; this.unlockController = unlockController; this.unlockedControllerProvider = unlockedControllerProvider; this.changePasswordController = changePasswordController; @@ -133,9 +135,8 @@ public class MainController extends AbstractFXMLViewController { removeVaultButton.disableProperty().bind(canEditSelectedVault); emptyListInstructions.visibleProperty().bind(Bindings.isEmpty(vaults)); - EasyBind.subscribe(activeController, this::activeControllerDidChange); EasyBind.subscribe(selectedVault, this::selectedVaultDidChange); - EasyBind.subscribe(isSelectedVaultUnlocked, this::selectedVaultUnlockedDidChange); + EasyBind.subscribe(activeController, this::activeControllerDidChange); EasyBind.subscribe(isShowingSettings, settingsButton::setSelected); EasyBind.subscribe(addVaultContextMenu.showingProperty(), addVaultButton::setSelected); } @@ -145,11 +146,6 @@ public class MainController extends AbstractFXMLViewController { return getClass().getResource("/fxml/main.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - private ListCell createDirecoryListCell(ListView param) { final DirectoryListCell cell = new DirectoryListCell(); cell.setVaultContextMenu(vaultListCellContextMenu); @@ -265,6 +261,10 @@ public class MainController extends AbstractFXMLViewController { } if (newValue.isUnlocked()) { this.showUnlockedView(newValue); + } else if (!newValue.doesVaultDirectoryExist()) { + this.showNotFoundView(); + } else if (newValue.isValidVaultDirectory() && newValue.needsUpgrade()) { + this.showUpgradeView(); } else if (newValue.isValidVaultDirectory()) { this.showUnlockView(); } else { @@ -272,29 +272,23 @@ public class MainController extends AbstractFXMLViewController { } } - private void selectedVaultUnlockedDidChange(Boolean unlocked) { - if (unlocked == null) { - // no vault selected -> no-op - } else if (unlocked) { - Platform.setImplicitExit(false); - this.showUnlockedView(selectedVault.get()); - } else { - this.showUnlockView(); - } - } - // **************************************** // Public Bindings // **************************************** public Binding windowTitle() { - return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(localization.getString("app.name")); + return EasyBind.monadic(selectedVault).flatMap(Vault::name).orElse(localization.getString("app.name")); } // **************************************** // Subcontroller for right panel // **************************************** + private void showNotFoundView() { + final NotFoundController ctrl = notFoundController.get(); + activeController.set(ctrl); + } + private void showInitializeView() { final InitializeController ctrl = initializeController.get(); ctrl.vault.bind(selectedVault); @@ -302,16 +296,35 @@ public class MainController extends AbstractFXMLViewController { activeController.set(ctrl); } - public void didInitialize(InitializeController ctrl) { + public void didInitialize() { + showUnlockView(); + } + + private void showUpgradeView() { + final UpgradeController ctrl = upgradeController.get(); + ctrl.vault.bind(selectedVault); + ctrl.setListener(this::didUpgrade); + activeController.set(ctrl); + } + + public void didUpgrade() { showUnlockView(); } private void showUnlockView() { final UnlockController ctrl = unlockController.get(); ctrl.vault.bind(selectedVault); + ctrl.setListener(this::didUnlock); activeController.set(ctrl); } + public void didUnlock(Vault vault) { + Platform.setImplicitExit(false); + if (vault.equals(selectedVault.getValue())) { + this.showUnlockedView(vault); + } + } + private void showUnlockedView(Vault vault) { final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> { return unlockedControllerProvider.get(); @@ -336,7 +349,7 @@ public class MainController extends AbstractFXMLViewController { activeController.set(ctrl); } - public void didChangePassword(ChangePasswordController ctrl) { + public void didChangePassword() { showUnlockView(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java new file mode 100644 index 000000000..20828b456 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java @@ -0,0 +1,23 @@ +package org.cryptomator.ui.controllers; + +import java.net.URL; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.cryptomator.ui.settings.Localization; + +@Singleton +public class NotFoundController extends LocalizedFXMLViewController { + + @Inject + public NotFoundController(Localization localization) { + super(localization); + } + + @Override + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/notfound.fxml"); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index 67094dd63..7af0e8269 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers; import java.net.URL; import java.util.Optional; -import java.util.ResourceBundle; import javax.inject.Inject; import javax.inject.Singleton; @@ -28,14 +27,13 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; @Singleton -public class SettingsController extends AbstractFXMLViewController { +public class SettingsController extends LocalizedFXMLViewController { - private final Localization localization; private final Settings settings; @Inject public SettingsController(Localization localization, Settings settings) { - this.localization = localization; + super(localization); this.settings = settings; } @@ -44,7 +42,7 @@ public class SettingsController extends AbstractFXMLViewController { @FXML private TextField portField; - + @FXML private CheckBox useIpv6Checkbox; @@ -71,11 +69,6 @@ public class SettingsController extends AbstractFXMLViewController { return getClass().getResource("/fxml/settings.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - private Optional applicationVersion() { return Optional.ofNullable(getClass().getPackage().getImplementationVersion()); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java index bead49f46..2844e24f4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java @@ -10,7 +10,7 @@ package org.cryptomator.ui.controllers; import java.net.URL; import java.util.Comparator; -import java.util.ResourceBundle; +import java.util.Optional; import java.util.concurrent.ExecutorService; import javax.inject.Inject; @@ -51,23 +51,23 @@ import javafx.scene.layout.GridPane; import javafx.scene.text.Text; import javafx.util.StringConverter; -public class UnlockController extends AbstractFXMLViewController { +public class UnlockController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); private final Application app; - private final Localization localization; private final ExecutorService exec; private final Lazy frontendFactory; private final Settings settings; private final WindowsDriveLetters driveLetters; private final ChangeListener driveLetterChangeListener = this::winDriveLetterDidChange; final ObjectProperty vault = new SimpleObjectProperty<>(); + private Optional listener = Optional.empty(); @Inject public UnlockController(Application app, Localization localization, ExecutorService exec, Lazy frontendFactory, Settings settings, WindowsDriveLetters driveLetters) { + super(localization); this.app = app; - this.localization = localization; this.exec = exec; this.frontendFactory = frontendFactory; this.settings = settings; @@ -127,11 +127,6 @@ public class UnlockController extends AbstractFXMLViewController { return getClass().getResource("/fxml/unlock.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - private void vaultChanged(Vault newVault) { if (newVault == null) { return; @@ -276,21 +271,24 @@ public class UnlockController extends AbstractFXMLViewController { progressIndicator.setVisible(true); downloadsPageLink.setVisible(false); CharSequence password = passwordField.getCharacters(); - exec.submit(() -> this.unlock(password)); + exec.submit(() -> this.unlock(vault.get(), password)); } - private void unlock(CharSequence password) { + private void unlock(Vault vault, CharSequence password) { try { - vault.get().activateFrontend(frontendFactory.get(), settings, password); - vault.get().reveal(); + vault.activateFrontend(frontendFactory.get(), settings, password); + vault.reveal(); + Platform.runLater(() -> { + messageText.setText(null); + listener.ifPresent(lstnr -> lstnr.didUnlock(vault)); + }); } catch (InvalidPassphraseException e) { Platform.runLater(() -> { messageText.setText(localization.getString("unlock.errorMessage.wrongPassword")); passwordField.requestFocus(); }); } catch (UnsupportedVaultFormatException e) { - LOG.warn("Unable to unlock vault: " + e.getMessage()); Platform.runLater(() -> { downloadsPageLink.setVisible(true); if (e.isVaultOlderThanSoftware()) { @@ -314,4 +312,15 @@ public class UnlockController extends AbstractFXMLViewController { } } + /* callback */ + + public void setListener(UnlockListener listener) { + this.listener = Optional.ofNullable(listener); + } + + @FunctionalInterface + interface UnlockListener { + void didUnlock(Vault vault); + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java index 9ae551fdc..9cc58ecf1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java @@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers; import java.net.URL; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import javax.inject.Inject; @@ -46,12 +45,11 @@ import javafx.stage.PopupWindow.AnchorLocation; import javafx.stage.Stage; import javafx.util.Duration; -public class UnlockedController extends AbstractFXMLViewController { +public class UnlockedController extends LocalizedFXMLViewController { private static final int IO_SAMPLING_STEPS = 100; private static final double IO_SAMPLING_INTERVAL = 0.25; - private final Localization localization; private final Stage macWarningsWindow = new Stage(); private final MacWarningsController macWarningsController; private final ExecutorService exec; @@ -61,7 +59,7 @@ public class UnlockedController extends AbstractFXMLViewController { @Inject public UnlockedController(Localization localization, Provider macWarningsControllerProvider, ExecutorService exec) { - this.localization = localization; + super(localization); this.macWarningsController = macWarningsControllerProvider.get(); this.exec = exec; @@ -97,11 +95,6 @@ public class UnlockedController extends AbstractFXMLViewController { return getClass().getResource("/fxml/unlocked.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - private void vaultChanged(Vault newVault) { if (newVault == null) { return; @@ -261,10 +254,6 @@ public class UnlockedController extends AbstractFXMLViewController { /* callback */ - public LockListener getListener() { - return listener.orElse(null); - } - public void setListener(LockListener listener) { this.listener = Optional.ofNullable(listener); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java new file mode 100644 index 000000000..9dcc57d0f --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java @@ -0,0 +1,128 @@ +package org.cryptomator.ui.controllers; + +import java.net.URL; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutorService; + +import javax.inject.Inject; + +import org.cryptomator.ui.model.UpgradeInstruction; +import org.cryptomator.ui.model.UpgradeInstruction.UpgradeFailedException; +import org.cryptomator.ui.model.Vault; +import org.cryptomator.ui.settings.Localization; +import org.fxmisc.easybind.EasyBind; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javafx.application.Platform; +import javafx.beans.binding.Binding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressIndicator; + +public class UpgradeController extends LocalizedFXMLViewController { + + private static final Logger LOG = LoggerFactory.getLogger(UpgradeController.class); + + final ObjectProperty vault = new SimpleObjectProperty<>(); + private final ExecutorService exec; + private final Binding> upgradeInstruction = EasyBind.monadic(vault).map(Vault::availableUpgrade); + private Optional listener = Optional.empty(); + + @Inject + public UpgradeController(Localization localization, ExecutorService exec) { + super(localization); + this.exec = exec; + } + + @FXML + private Label upgradeLabel; + + @FXML + private Button upgradeButton; + + @FXML + private ProgressIndicator progressIndicator; + + @FXML + private Label errorLabel; + + @Override + protected void initialize() { + upgradeLabel.textProperty().bind(EasyBind.monadic(upgradeInstruction).map(instruction -> { + return instruction.map(this::upgradeNotification).orElse(""); + }).orElse("")); + + EasyBind.subscribe(vault, this::vaultChanged); + } + + @Override + protected URL getFxmlResourceUrl() { + return getClass().getResource("/fxml/upgrade.fxml"); + } + + private void vaultChanged(Vault newVault) { + errorLabel.setText(null); + } + + // **************************************** + // Upgrade label + // **************************************** + + private String upgradeNotification(UpgradeInstruction instruction) { + return instruction.getNotification(vault.get(), localization); + } + + // **************************************** + // Upgrade button + // **************************************** + + @FXML + private void didClickUpgradeButton(ActionEvent event) { + upgradeInstruction.getValue().ifPresent(this::upgrade); + } + + private void upgrade(UpgradeInstruction instruction) { + Vault v = vault.getValue(); + Objects.requireNonNull(v); + progressIndicator.setVisible(true); + upgradeButton.setDisable(true); + exec.submit(() -> { + if (!instruction.isApplicable(v)) { + LOG.error("No upgrade needed for " + v.path().getValue()); + throw new IllegalStateException("No ugprade needed for " + v.path().getValue()); + } + try { + instruction.upgrade(v, localization); + Platform.runLater(() -> { + progressIndicator.setVisible(false); + upgradeButton.setDisable(false); + listener.ifPresent(UpgradeListener::didUpgrade); + }); + } catch (UpgradeFailedException e) { + Platform.runLater(() -> { + errorLabel.setText(e.getLocalizedMessage()); + progressIndicator.setVisible(false); + upgradeButton.setDisable(false); + }); + } + }); + } + + /* callback */ + + public void setListener(UpgradeListener listener) { + this.listener = Optional.ofNullable(listener); + } + + @FunctionalInterface + interface UpgradeListener { + void didUpgrade(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java index 16c2fa607..5c976e317 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java @@ -15,7 +15,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import javax.inject.Inject; @@ -48,20 +47,19 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; @Singleton -public class WelcomeController extends AbstractFXMLViewController { +public class WelcomeController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); private final Application app; - private final Localization localization; private final Settings settings; private final Comparator semVerComparator; private final ExecutorService executor; @Inject public WelcomeController(Application app, Localization localization, Settings settings, @Named("SemVer") Comparator semVerComparator, ExecutorService executor) { + super(localization); this.app = app; - this.localization = localization; this.settings = settings; this.semVerComparator = semVerComparator; this.executor = executor; @@ -93,11 +91,6 @@ public class WelcomeController extends AbstractFXMLViewController { return getClass().getResource("/fxml/welcome.fxml"); } - @Override - protected ResourceBundle getFxmlResourceBundle() { - return localization; - } - // **************************************** // Check for updates // **************************************** diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java b/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java index 125f884ba..43bdc4359 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java @@ -9,9 +9,8 @@ package org.cryptomator.ui.controls; import org.cryptomator.ui.model.Vault; +import org.fxmisc.easybind.EasyBind; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.geometry.Pos; import javafx.scene.control.ContentDisplay; import javafx.scene.control.ContextMenu; @@ -21,15 +20,18 @@ import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; -public class DirectoryListCell extends DraggableListCellimplements ChangeListener { +public class DirectoryListCell extends DraggableListCell { // fill: #FD4943, stroke: #E1443F private static final Color RED_FILL = Color.rgb(253, 73, 67); private static final Color RED_STROKE = Color.rgb(225, 68, 63); + // fill: #FFBF2F, stroke: #E4AC36 + // private static final Color YELLOW_FILL = Color.rgb(255, 191, 47); + // private static final Color YELLOW_STROKE = Color.rgb(228, 172, 54); + // fill: #28CA40, stroke: #30B740 private static final Color GREEN_FILL = Color.rgb(40, 202, 64); private static final Color GREEN_STROKE = Color.rgb(48, 183, 64); @@ -46,58 +48,38 @@ public class DirectoryListCell extends DraggableListCellimplements Change hbox.setPrefWidth(1); vbox.setFillWidth(true); + nameText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::name)); nameText.textFillProperty().bind(this.textFillProperty()); nameText.fontProperty().bind(this.fontProperty()); + pathText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath)); pathText.setTextOverrun(OverrunStyle.ELLIPSIS); pathText.getStyleClass().add("detail-label"); + statusIndicator.fillProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> { + return unlocked ? GREEN_FILL : RED_FILL; + })); + + statusIndicator.strokeProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> { + return unlocked ? GREEN_STROKE : RED_STROKE; + })); + + tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::path).map(p -> new Tooltip(p.toString()))); + contextMenuProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> { + return unlocked ? null : vaultContextMenu; + })); + setGraphic(hbox); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } @Override protected void updateItem(Vault item, boolean empty) { - final Vault oldItem = super.getItem(); - if (oldItem != null) { - oldItem.unlockedProperty().removeListener(this); - } super.updateItem(item, empty); if (item == null) { - nameText.setText(null); - pathText.setText(null); - setTooltip(null); - setContextMenu(null); statusIndicator.setVisible(false); } else { - nameText.setText(item.getName()); - pathText.setText(item.getDisplayablePath()); - setTooltip(new Tooltip(item.getPath().toString())); statusIndicator.setVisible(true); - item.unlockedProperty().addListener(this); - updateStatusIndicator(); - updateContextMenu(); - } - } - - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - updateStatusIndicator(); - updateContextMenu(); - } - - private void updateStatusIndicator() { - final Paint fillColor = getItem().isUnlocked() ? GREEN_FILL : RED_FILL; - final Paint strokeColor = getItem().isUnlocked() ? GREEN_STROKE : RED_STROKE; - statusIndicator.setFill(fillColor); - statusIndicator.setStroke(strokeColor); - } - - private void updateContextMenu() { - if (getItem().isUnlocked()) { - this.setContextMenu(null); - } else { - this.setContextMenu(vaultContextMenu); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java new file mode 100644 index 000000000..a903c1369 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java @@ -0,0 +1,40 @@ +package org.cryptomator.ui.model; + +import org.cryptomator.ui.settings.Localization; + +public interface UpgradeInstruction { + + static UpgradeInstruction[] AVAILABLE_INSTRUCTIONS = {new UpgradeVersion3DropBundleExtension()}; + + /** + * @return Localized string to display to the user when an upgrade is needed. + */ + String getNotification(Vault vault, Localization localization); + + /** + * Upgrades a vault. Might take a moment, should be run in a background thread. + */ + void upgrade(Vault vault, Localization localization) throws UpgradeFailedException; + + /** + * Determines in O(1), if an upgrade can be applied to a vault. + * + * @return true if and only if the vault can be migrated to a newer version without the risk of data losses. + */ + boolean isApplicable(Vault vault); + + /** + * Thrown when data migration failed. + */ + public class UpgradeFailedException extends Exception { + + UpgradeFailedException() { + } + + UpgradeFailedException(String message) { + super(message); + } + + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java new file mode 100644 index 000000000..36a21b1fb --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java @@ -0,0 +1,55 @@ +package org.cryptomator.ui.model; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.lang3.StringUtils; +import org.cryptomator.ui.settings.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javafx.application.Platform; + +class UpgradeVersion3DropBundleExtension implements UpgradeInstruction { + + private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class); + + @Override + public String getNotification(Vault vault, Localization localization) { + String fmt = localization.getString("upgrade.version3dropBundleExtension.msg"); + Path path = vault.path().getValue(); + String oldVaultName = path.getFileName().toString(); + String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION); + return String.format(fmt, oldVaultName, newVaultName); + } + + @Override + public void upgrade(Vault vault, Localization localization) throws UpgradeFailedException { + Path path = vault.path().getValue(); + String oldVaultName = path.getFileName().toString(); + String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION); + Path newPath = path.resolveSibling(newVaultName); + if (Files.exists(newPath)) { + String fmt = localization.getString("upgrade.version3dropBundleExtension.err.alreadyExists"); + String msg = String.format(fmt, newPath); + throw new UpgradeFailedException(msg); + } else { + try { + Files.move(path, path.resolveSibling(newVaultName)); + Platform.runLater(() -> { + vault.setPath(newPath); + }); + } catch (IOException e) { + LOG.error("Vault migration failed", e); + throw new UpgradeFailedException(); + } + } + } + + @Override + public boolean isApplicable(Vault vault) { + return vault.path().getValue().getFileName().toString().endsWith(Vault.VAULT_FILE_EXTENSION); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index 56483cfd0..0fb0d8b76 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -11,10 +11,12 @@ package org.cryptomator.ui.model; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.Normalizer; import java.text.Normalizer.Form; +import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -42,12 +44,17 @@ import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.util.DeferredClosable; import org.cryptomator.ui.util.DeferredCloser; import org.cryptomator.ui.util.FXThreads; +import org.fxmisc.easybind.EasyBind; import com.google.common.collect.ImmutableMap; import javafx.application.Platform; +import javafx.beans.binding.Binding; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -55,7 +62,7 @@ public class Vault implements CryptoFileSystemDelegate { public static final String VAULT_FILE_EXTENSION = ".cryptomator"; - private final Path path; + private final ObjectProperty path; private final DeferredCloser closer; private final ShorteningFileSystemFactory shorteningFileSystemFactory; private final CryptoFileSystemFactory cryptoFileSystemFactory; @@ -73,20 +80,20 @@ public class Vault implements CryptoFileSystemDelegate { * Package private constructor, use {@link VaultFactory}. */ Vault(Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) { - this.path = vaultDirectoryPath; + this.path = new SimpleObjectProperty(vaultDirectoryPath); this.closer = closer; this.shorteningFileSystemFactory = shorteningFileSystemFactory; this.cryptoFileSystemFactory = cryptoFileSystemFactory; try { - setMountName(getName()); + setMountName(name().getValue()); } catch (IllegalArgumentException e) { // mount name needs to be set by the user explicitly later } } private FileSystem getNioFileSystem() { - return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path)); + return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path.getValue())); } // ****************************************************************************** @@ -158,6 +165,16 @@ public class Vault implements CryptoFileSystemDelegate { Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount); } + public boolean needsUpgrade() { + return availableUpgrade().isPresent(); + } + + public Optional availableUpgrade() { + return Arrays.stream(UpgradeInstruction.AVAILABLE_INSTRUCTIONS).filter(instruction -> { + return instruction.isApplicable(this); + }).findAny(); + } + // ****************************************************************************** // Delegate methods // ********************************************************************************/ @@ -180,31 +197,42 @@ public class Vault implements CryptoFileSystemDelegate { return filesystemFrontend.get().map(Frontend::getWebDavUrl).orElseThrow(IllegalStateException::new); } - public Path getPath() { + void setPath(Path path) { + this.path.set(path); + this.nioFileSystem.set(null); + } + + public ReadOnlyObjectProperty path() { return path; } - public String getDisplayablePath() { + public Binding displayablePath() { Path homeDir = Paths.get(SystemUtils.USER_HOME); - if (path.startsWith(homeDir)) { - Path relativePath = homeDir.relativize(path); - String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; - return homePrefix + relativePath.toString(); - } else { - return path.toString(); - } + return EasyBind.map(path, p -> { + if (p.startsWith(homeDir)) { + Path relativePath = homeDir.relativize(p); + String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; + return homePrefix + relativePath.toString(); + } else { + return path.toString(); + } + }); } /** * @return Directory name without preceeding path components and file extension */ - public String getName() { - return StringUtils.removeEnd(path.getFileName().toString(), VAULT_FILE_EXTENSION); + public Binding name() { + return EasyBind.map(path, p -> p.getFileName().toString()); + } + + public boolean doesVaultDirectoryExist() { + return Files.isDirectory(path.getValue()); } public boolean isValidVaultDirectory() { try { - return cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem()); + return doesVaultDirectoryExist() && cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem()); } catch (UncheckedIOException e) { return false; } @@ -292,17 +320,17 @@ public class Vault implements CryptoFileSystemDelegate { @Override public int hashCode() { - return path.hashCode(); + return path.getValue().hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Vault) { final Vault other = (Vault) obj; - return this.path.equals(other.path); + return this.path.getValue().equals(other.path.getValue()); } else { return false; } } -} +} \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java index fc3fb0ca7..d71626cf6 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java @@ -55,7 +55,7 @@ public class VaultObjectMapperProvider implements Provider { @Override public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); - jgen.writeStringField("path", value.getPath().toString()); + jgen.writeStringField("path", value.path().getValue().toString()); jgen.writeStringField("mountName", value.getMountName()); final Character winDriveLetter = value.getWinDriveLetter(); if (winDriveLetter != null) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java index 7e1c6660e..9d40ac09d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java @@ -77,7 +77,6 @@ public class SettingsProvider implements Provider { final Path settingsPath = getSettingsPath(); final InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); settings = objectMapper.readValue(in, Settings.class); - settings.getDirectories().removeIf(v -> !v.isValidVaultDirectory()); } catch (IOException e) { LOG.warn("Failed to load settings, creating new one."); settings = new Settings(); diff --git a/main/ui/src/main/resources/fxml/notfound.fxml b/main/ui/src/main/resources/fxml/notfound.fxml new file mode 100644 index 000000000..c4f8208f7 --- /dev/null +++ b/main/ui/src/main/resources/fxml/notfound.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/main/ui/src/main/resources/fxml/upgrade.fxml b/main/ui/src/main/resources/fxml/upgrade.fxml new file mode 100644 index 000000000..b153c4f6a --- /dev/null +++ b/main/ui/src/main/resources/fxml/upgrade.fxml @@ -0,0 +1,27 @@ + + + + + + + + + + +