mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 18:21:26 +00:00
restore config by recoverykey
auto restore when bkup exists added new VaultStates
This commit is contained in:
@@ -70,6 +70,8 @@ public class Vault {
|
||||
private final BooleanBinding missing;
|
||||
private final BooleanBinding needsMigration;
|
||||
private final BooleanBinding unknownError;
|
||||
private final BooleanBinding missingMasterkey;
|
||||
private final BooleanBinding missingVaultConfig;
|
||||
private final ObjectBinding<Mountpoint> mountPoint;
|
||||
private final Mounter mounter;
|
||||
private final Settings settings;
|
||||
@@ -96,6 +98,8 @@ public class Vault {
|
||||
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
|
||||
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
|
||||
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
|
||||
this.missingMasterkey = Bindings.createBooleanBinding(this::isMissingMasterkey, state);
|
||||
this.missingVaultConfig = Bindings.createBooleanBinding(this::isMissingVaultConfig, state);
|
||||
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
||||
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
||||
@@ -315,6 +319,22 @@ public class Vault {
|
||||
return state.get() == VaultState.Value.ERROR;
|
||||
}
|
||||
|
||||
public BooleanBinding missingMasterkeyProperty() {
|
||||
return missingMasterkey;
|
||||
}
|
||||
|
||||
public boolean isMissingMasterkey() {
|
||||
return state.get() == VaultState.Value.MASTERKEY_MISSING;
|
||||
}
|
||||
|
||||
public BooleanBinding missingVaultConfigProperty() {
|
||||
return missingVaultConfig;
|
||||
}
|
||||
|
||||
public boolean isMissingVaultConfig() {
|
||||
return state.get() == VaultState.Value.VAULT_CONFIG_MISSING;
|
||||
}
|
||||
|
||||
public ReadOnlyStringProperty displayNameProperty() {
|
||||
return vaultSettings.displayName;
|
||||
}
|
||||
|
||||
@@ -25,15 +25,20 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
|
||||
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.MASTERKEY_MISSING;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
|
||||
|
||||
@Singleton
|
||||
public class VaultListManager {
|
||||
@@ -129,7 +134,7 @@ public class VaultListManager {
|
||||
VaultState state = vault.stateProperty();
|
||||
VaultState.Value previousState = state.getValue();
|
||||
return switch (previousState) {
|
||||
case LOCKED, NEEDS_MIGRATION, MISSING -> {
|
||||
case LOCKED, NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, MASTERKEY_MISSING -> {
|
||||
try {
|
||||
var determinedState = determineVaultState(vault.getPath());
|
||||
if (determinedState == LOCKED) {
|
||||
@@ -149,7 +154,56 @@ public class VaultListManager {
|
||||
}
|
||||
|
||||
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
|
||||
if (!Files.exists(pathToVault)) {
|
||||
Path pathToVaultConfig = Path.of(pathToVault.toString(),"vault.cryptomator");
|
||||
Path pathToMasterkey = Path.of(pathToVault.toString(),"masterkey.cryptomator");
|
||||
if (!Files.exists(pathToVaultConfig)) {
|
||||
try (Stream<Path> files = Files.list(pathToVaultConfig.getParent())) {
|
||||
Path backupFile = files.filter(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
return fileName.startsWith("vault.cryptomator") && fileName.endsWith(".bkup");
|
||||
}).findFirst().orElse(null);
|
||||
|
||||
if (backupFile != null) {
|
||||
try {
|
||||
Files.copy(backupFile, pathToVaultConfig, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
LOG.error("error",e);
|
||||
return VAULT_CONFIG_MISSING;
|
||||
}
|
||||
} else {
|
||||
return VAULT_CONFIG_MISSING;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("error",e);
|
||||
return VAULT_CONFIG_MISSING;
|
||||
}
|
||||
|
||||
}
|
||||
else if (!Files.exists(pathToMasterkey)) {
|
||||
//return VaultState.Value.MASTERKEY_MISSING;
|
||||
try (Stream<Path> files = Files.list(pathToMasterkey.getParent())) {
|
||||
Path backupFile = files.filter(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
return fileName.startsWith("masterkey.cryptomator") && fileName.endsWith(".bkup");
|
||||
}).findFirst().orElse(null);
|
||||
|
||||
if (backupFile != null) {
|
||||
try {
|
||||
Files.copy(backupFile, pathToMasterkey, StandardCopyOption.REPLACE_EXISTING);
|
||||
return MASTERKEY_MISSING;
|
||||
} catch (IOException e) {
|
||||
LOG.error("error",e);
|
||||
return MASTERKEY_MISSING;
|
||||
}
|
||||
} else {
|
||||
return MASTERKEY_MISSING;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("error",e);
|
||||
return MASTERKEY_MISSING;
|
||||
}
|
||||
}
|
||||
else if (!Files.exists(pathToVault)) {
|
||||
return VaultState.Value.MISSING;
|
||||
}
|
||||
return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
|
||||
|
||||
@@ -25,6 +25,10 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
||||
*/
|
||||
MISSING,
|
||||
|
||||
VAULT_CONFIG_MISSING,
|
||||
|
||||
MASTERKEY_MISSING,
|
||||
|
||||
/**
|
||||
* Vault requires migration to a newer vault format
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.cryptomator.ui.common.StageInitializer;
|
||||
import org.cryptomator.ui.error.ErrorComponent;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
import org.cryptomator.ui.migration.MigrationComponent;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
||||
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
||||
@@ -30,7 +31,7 @@ import javafx.stage.Stage;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
|
||||
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class, RecoveryKeyComponent.class})
|
||||
abstract class MainWindowModule {
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -53,6 +53,8 @@ public class VaultDetailController implements FxController {
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
case VAULT_CONFIG_MISSING -> FontAwesome5Icon.COGS;
|
||||
case MASTERKEY_MISSING -> FontAwesome5Icon.KEY;
|
||||
};
|
||||
} else {
|
||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.mainwindow;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -22,14 +23,17 @@ public class VaultDetailMissingVaultController implements FxController {
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Stage window;
|
||||
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
|
||||
|
||||
|
||||
|
||||
@Inject
|
||||
public VaultDetailMissingVaultController(ObjectProperty<Vault> vault, RemoveVaultComponent.Builder removeVault, ResourceBundle resourceBundle, @MainWindow Stage window) {
|
||||
public VaultDetailMissingVaultController(ObjectProperty<Vault> vault, RemoveVaultComponent.Builder removeVault, ResourceBundle resourceBundle, @MainWindow Stage window, RecoveryKeyComponent.Factory recoveryKeyWindow) {
|
||||
this.vault = vault;
|
||||
this.removeVault = removeVault;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.window = window;
|
||||
this.recoveryKeyWindow = recoveryKeyWindow;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -42,6 +46,15 @@ public class VaultDetailMissingVaultController implements FxController {
|
||||
removeVault.vault(vault.get()).build().showRemoveVault();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void restoreVaultConfig(){
|
||||
recoveryKeyWindow.create(vault.get(), window).showRecoveryKeyRecoverWindow("Recover VaultConfig");
|
||||
}
|
||||
@FXML
|
||||
void restoreMasterkey(){
|
||||
recoveryKeyWindow.create(vault.get(), window).showRecoveryKeyRecoverWindow("Recover Masterkey");
|
||||
}
|
||||
|
||||
@FXML
|
||||
void changeLocation() {
|
||||
// copied from ChooseExistingVaultController class
|
||||
|
||||
@@ -47,6 +47,8 @@ public class VaultListCellController implements FxController {
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
case VAULT_CONFIG_MISSING -> FontAwesome5Icon.COGS;
|
||||
case MASTERKEY_MISSING -> FontAwesome5Icon.KEY;
|
||||
};
|
||||
} else {
|
||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
|
||||
@@ -38,6 +38,13 @@ public interface RecoveryKeyComponent {
|
||||
stage.show();
|
||||
}
|
||||
|
||||
default void showRecoveryKeyRecoverWindow(String title) {
|
||||
Stage stage = window();
|
||||
stage.setScene(recoverScene().get());
|
||||
stage.setTitle(title);
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
}
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
@@ -2,6 +2,13 @@ package org.cryptomator.ui.recoverykey;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
@@ -18,8 +25,17 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
|
||||
|
||||
@RecoveryKeyScoped
|
||||
public class RecoveryKeyResetPasswordController implements FxController {
|
||||
|
||||
@@ -32,11 +48,12 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
private final StringProperty recoveryKey;
|
||||
private final Lazy<Scene> recoverResetPasswordSuccessScene;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final MasterkeyFileAccess masterkeyFileAccess;
|
||||
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS) Lazy<Scene> recoverResetPasswordSuccessScene, FxApplicationWindows appWindows) {
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS) Lazy<Scene> recoverResetPasswordSuccessScene, FxApplicationWindows appWindows, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
@@ -44,6 +61,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
this.recoveryKey = recoveryKey;
|
||||
this.recoverResetPasswordSuccessScene = recoverResetPasswordSuccessScene;
|
||||
this.appWindows = appWindows;
|
||||
this.masterkeyFileAccess = masterkeyFileAccess;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -53,19 +71,60 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void resetPassword() {
|
||||
Task<Void> task = new ResetPasswordTask();
|
||||
task.setOnScheduled(event -> {
|
||||
LOG.debug("Using recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||
});
|
||||
task.setOnSucceeded(event -> {
|
||||
LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||
window.setScene(recoverResetPasswordSuccessScene.get());
|
||||
});
|
||||
task.setOnFailed(event -> {
|
||||
LOG.error("Resetting password failed.", task.getException());
|
||||
appWindows.showErrorWindow(task.getException(), window, null);
|
||||
});
|
||||
executor.submit(task);
|
||||
if(vault.isMissingVaultConfig()){
|
||||
Path vaultPath = vault.getPath();
|
||||
Path recoveryPath = vaultPath.resolve("r");
|
||||
try {
|
||||
Files.createDirectory(recoveryPath);
|
||||
recoveryKeyFactory.newMasterkeyFileWithPassphrase(recoveryPath, recoveryKey.get(), newPasswordController.passwordField.getCharacters());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Creating directory or recovering masterkey failed", e);
|
||||
}
|
||||
|
||||
Path masterkeyFilePath = recoveryPath.resolve(MASTERKEY_FILENAME);
|
||||
try (Masterkey masterkey = masterkeyFileAccess.load(masterkeyFilePath, newPasswordController.passwordField.getCharacters())) {
|
||||
try {
|
||||
MasterkeyLoader loader = ignored -> masterkey.copy();
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||
.withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC) //
|
||||
.withKeyLoader(loader) //
|
||||
.withShorteningThreshold(220) //
|
||||
.build();
|
||||
CryptoFileSystemProvider.initialize(recoveryPath, fsProps, DEFAULT_KEY_ID);
|
||||
} catch (CryptoException | IOException e) {
|
||||
LOG.error("Recovering vault failed", e);
|
||||
}
|
||||
Files.move(masterkeyFilePath, vaultPath.resolve(MASTERKEY_FILENAME), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(recoveryPath.resolve(VAULTCONFIG_FILENAME), vaultPath.resolve(VAULTCONFIG_FILENAME));
|
||||
try (var paths = Files.walk(recoveryPath)) {
|
||||
paths.sorted(Comparator.reverseOrder()).forEach(p -> {
|
||||
try {
|
||||
Files.delete(p);
|
||||
} catch (IOException e) {
|
||||
LOG.info("Unable to delete {}. Please delete it manually.", p);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.setScene(recoverResetPasswordSuccessScene.get());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Moving recovered files failed", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Task<Void> task = new ResetPasswordTask();
|
||||
task.setOnScheduled(event -> {
|
||||
LOG.debug("Using recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||
});
|
||||
task.setOnSucceeded(event -> {
|
||||
LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||
window.setScene(recoverResetPasswordSuccessScene.get());
|
||||
});
|
||||
task.setOnFailed(event -> {
|
||||
LOG.error("Resetting password failed.", task.getException());
|
||||
appWindows.showErrorWindow(task.getException(), window, null);
|
||||
});
|
||||
executor.submit(task);
|
||||
}
|
||||
}
|
||||
|
||||
private class ResetPasswordTask extends Task<Void> {
|
||||
|
||||
@@ -53,5 +53,7 @@
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_missing.fxml" visible="${controller.vault.missing}" managed="${controller.vault.missing}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_needsmigration.fxml" visible="${controller.vault.needsMigration}" managed="${controller.vault.needsMigration}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_unknownerror.fxml" visible="${controller.vault.unknownError}" managed="${controller.vault.unknownError}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_missing_masterkey.fxml" visible="${controller.vault.missingMasterkey}" managed="${controller.vault.missingMasterkey}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_missing_vault_config.fxml" visible="${controller.vault.missingVaultConfig}" managed="${controller.vault.missingVaultConfig}"/>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
44
src/main/resources/fxml/vault_detail_missing_masterkey.fxml
Normal file
44
src/main/resources/fxml/vault_detail_missing_masterkey.fxml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.mainwindow.VaultDetailMissingVaultController"
|
||||
alignment="TOP_CENTER"
|
||||
spacing="9">
|
||||
<children>
|
||||
<VBox spacing="9" alignment="CENTER">
|
||||
<StackPane alignment="CENTER">
|
||||
<Circle styleClass="glyph-icon-primary" radius="48"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="FILE" glyphSize="48"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-primary" glyph="SEARCH" glyphSize="24">
|
||||
<StackPane.margin>
|
||||
<Insets top="12"/>
|
||||
</StackPane.margin>
|
||||
</FontAwesome5IconView>
|
||||
</StackPane>
|
||||
<Label text="%main.vaultDetail.missingMasterkey.info" wrapText="true"/>
|
||||
</VBox>
|
||||
<VBox spacing="6" alignment="CENTER">
|
||||
<Button text="%main.vaultDetail.missing.recheck" minWidth="120" onAction="#recheck">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="REDO"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button text="%main.vaultDetail.missingMasterkey.restore" minWidth="120" onAction="#restoreMasterkey">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="MAGIC"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button text="%main.vaultDetail.missing.remove" minWidth="120" onAction="#didClickRemoveVault">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TRASH"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.mainwindow.VaultDetailMissingVaultController"
|
||||
alignment="TOP_CENTER"
|
||||
spacing="9">
|
||||
<children>
|
||||
<VBox spacing="9" alignment="CENTER">
|
||||
<StackPane alignment="CENTER">
|
||||
<Circle styleClass="glyph-icon-primary" radius="48"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="FILE" glyphSize="48"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-primary" glyph="SEARCH" glyphSize="24">
|
||||
<StackPane.margin>
|
||||
<Insets top="12"/>
|
||||
</StackPane.margin>
|
||||
</FontAwesome5IconView>
|
||||
</StackPane>
|
||||
<Label text="%main.vaultDetail.missingVaultConfig.info" wrapText="true"/>
|
||||
</VBox>
|
||||
<VBox spacing="6" alignment="CENTER">
|
||||
<Button text="%main.vaultDetail.missing.recheck" minWidth="120" onAction="#recheck">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="REDO"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button text="%main.vaultDetail.missingVaultConfig.restore" minWidth="120" onAction="#restoreVaultConfig">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="MAGIC"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button text="%main.vaultDetail.missing.remove" minWidth="120" onAction="#didClickRemoveVault">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TRASH"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -429,6 +429,13 @@ main.vaultDetail.missing.info=Cryptomator could not find a vault at this path.
|
||||
main.vaultDetail.missing.recheck=Recheck
|
||||
main.vaultDetail.missing.remove=Remove from Vault List…
|
||||
main.vaultDetail.missing.changeLocation=Change Vault Location…
|
||||
### Missing Vault Config
|
||||
main.vaultDetail.missingVaultConfig.info=VaultConfig is missing.
|
||||
main.vaultDetail.missingVaultConfig.restore=Restore VaultConfig
|
||||
### Missing Masterkey
|
||||
main.vaultDetail.missingMasterkey.info=Masterkey is missing.
|
||||
main.vaultDetail.missingMasterkey.restore=Restore Masterkey
|
||||
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=Upgrade Vault
|
||||
main.vaultDetail.migratePrompt=Your vault needs to be upgraded to a new format, before you can access it
|
||||
|
||||
Reference in New Issue
Block a user