mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-05 07:37:40 +00:00
Merge branch 'develop' into feature/fuse-on-win
Fixed conflicts in WebDavVolume
This commit is contained in:
+4
-3
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import dagger.Lazy;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -83,11 +84,11 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
public void initialize() {
|
||||
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
||||
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
||||
vaultPath.addListener(this::vaultPathDidChange);
|
||||
EasyBind.subscribe(vaultPath, this::vaultPathDidChange);
|
||||
}
|
||||
|
||||
private void vaultPathDidChange(@SuppressWarnings("unused") ObservableValue<? extends Path> observable, @SuppressWarnings("unused") Path oldValue, Path newValue) {
|
||||
if (!Files.notExists(newValue)) {
|
||||
private void vaultPathDidChange(Path newValue) {
|
||||
if ( newValue != null && !Files.notExists(newValue)) {
|
||||
warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
||||
} else {
|
||||
warningText.set(null);
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ import javax.inject.Named;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ public class ChangePasswordController implements FxController {
|
||||
public void finish() {
|
||||
try {
|
||||
CryptoFileSystemProvider.changePassphrase(vault.getPath(), MASTERKEY_FILENAME, oldPasswordField.getCharacters(), newPassword.get());
|
||||
LOG.info("Successfully changed password for {}", vault.getDisplayableName());
|
||||
LOG.info("Successfully changed password for {}", vault.getDisplayName());
|
||||
window.close();
|
||||
updatePasswordInSystemkeychain();
|
||||
} catch (IOException e) {
|
||||
@@ -85,7 +85,7 @@ public class ChangePasswordController implements FxController {
|
||||
if (keychain.isPresent()) {
|
||||
try {
|
||||
keychain.get().changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get()));
|
||||
LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayableName());
|
||||
LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName());
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to update password in system keychain.", e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -10,7 +11,6 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@@ -24,10 +24,12 @@ public class NewPasswordController implements FxController {
|
||||
public NiceSecurePasswordField passwordField;
|
||||
public NiceSecurePasswordField reenterField;
|
||||
public Label passwordStrengthLabel;
|
||||
public FontAwesome5IconView passwordStrengthCheckmark;
|
||||
public FontAwesome5IconView passwordStrengthWarning;
|
||||
public FontAwesome5IconView passwordStrengthCross;
|
||||
public Label passwordMatchLabel;
|
||||
public FontAwesome5IconView checkmark;
|
||||
public FontAwesome5IconView warning;
|
||||
public FontAwesome5IconView cross;
|
||||
public FontAwesome5IconView passwordMatchCheckmark;
|
||||
public FontAwesome5IconView passwordMatchCross;
|
||||
|
||||
public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ObjectProperty<CharSequence> password) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
@@ -45,7 +47,7 @@ public class NewPasswordController implements FxController {
|
||||
BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::hasSamePasswordInBothFields, passwordField.textProperty(), reenterField.textProperty());
|
||||
BooleanBinding reenterFieldNotEmpty = reenterField.textProperty().isNotEmpty();
|
||||
passwordMatchLabel.visibleProperty().bind(reenterFieldNotEmpty);
|
||||
passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(checkmark).otherwise(cross));
|
||||
passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(passwordMatchCheckmark).otherwise(passwordMatchCross));
|
||||
passwordMatchLabel.textProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(resourceBundle.getString("newPassword.passwordsMatch")).otherwise(resourceBundle.getString("newPassword.passwordsDoNotMatch")));
|
||||
|
||||
passwordField.textProperty().addListener(this::passwordsDidChange);
|
||||
@@ -56,11 +58,11 @@ public class NewPasswordController implements FxController {
|
||||
if (passwordField.getCharacters().length() == 0) {
|
||||
return null;
|
||||
} else if (passwordStrength.intValue() <= -1) {
|
||||
return cross;
|
||||
return passwordStrengthCross;
|
||||
} else if (passwordStrength.intValue() < 3) {
|
||||
return warning;
|
||||
return passwordStrengthWarning;
|
||||
} else {
|
||||
return checkmark;
|
||||
return passwordStrengthCheckmark;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ public class VaultService {
|
||||
*/
|
||||
public Task<Vault> createRevealTask(Vault vault) {
|
||||
Task<Vault> task = new RevealVaultTask(vault);
|
||||
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayableName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayableName(), evt.getSource().getException()));
|
||||
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ public class VaultService {
|
||||
*/
|
||||
public Task<Vault> createLockTask(Vault vault, boolean forced) {
|
||||
Task<Vault> task = new LockVaultTask(vault, forced);
|
||||
task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayableName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to lock " + vault.getDisplayableName(), evt.getSource().getException()));
|
||||
task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to lock " + vault.getDisplayName(), evt.getSource().getException()));
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class VaultService {
|
||||
List<Task<Vault>> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList());
|
||||
lockTasks.forEach(executorService::execute);
|
||||
Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks);
|
||||
String vaultNames = vaults.stream().map(Vault::getDisplayableName).collect(Collectors.joining(", "));
|
||||
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
|
||||
task.setOnSucceeded(evt -> LOG.info("Locked {}", vaultNames));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to lock vaults " + vaultNames, evt.getSource().getException()));
|
||||
return task;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.scene.SnapshotParameters;
|
||||
@@ -18,7 +19,6 @@ import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.Dragboard;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
public class PasswordStrengthIndicator extends HBox {
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public class ForgetPasswordController implements FxController {
|
||||
if (keychain.isPresent()) {
|
||||
try {
|
||||
keychain.get().deletePassphrase(vault.getId());
|
||||
LOG.debug("Forgot password for vault {}.", vault.getDisplayableName());
|
||||
LOG.debug("Forgot password for vault {}.", vault.getDisplayName());
|
||||
confirmedResult.setValue(true);
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to remove entry from system keychain.", e);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import dagger.Lazy;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
@@ -64,7 +65,7 @@ public class FxApplication extends Application {
|
||||
LOG.trace("FxApplication.start()");
|
||||
Platform.setImplicitExit(false);
|
||||
|
||||
hasVisibleStages.addListener(this::hasVisibleStagesChanged);
|
||||
EasyBind.subscribe(hasVisibleStages, this::hasVisibleStagesChanged);
|
||||
|
||||
settings.theme().addListener(this::themeChanged);
|
||||
loadSelectedStyleSheet(settings.theme().get());
|
||||
@@ -75,7 +76,7 @@ public class FxApplication extends Application {
|
||||
throw new UnsupportedOperationException("Use start() instead.");
|
||||
}
|
||||
|
||||
private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
|
||||
private void hasVisibleStagesChanged(boolean newValue) {
|
||||
if (newValue) {
|
||||
macFunctions.map(MacFunctions::uiState).ifPresent(MacApplicationUiState::transformToForegroundApplication);
|
||||
} else {
|
||||
@@ -100,7 +101,7 @@ public class FxApplication extends Application {
|
||||
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
unlockWindowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
|
||||
LOG.debug("Showing UnlockWindow for {}", vault.getDisplayableName());
|
||||
LOG.debug("Showing UnlockWindow for {}", vault.getDisplayName());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -10,7 +11,6 @@ import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -26,17 +26,23 @@ public class VaultDetailController implements FxController {
|
||||
VaultDetailController(ObjectProperty<Vault> vault, FxApplication application) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.glyph = EasyBind.select(vault).selectObject(Vault::stateProperty).map(this::getGlyphForVaultState).orElse(FontAwesome5Icon.EXCLAMATION_TRIANGLE);
|
||||
this.glyph = EasyBind.select(vault) //
|
||||
.selectObject(Vault::stateProperty) //
|
||||
.map(this::getGlyphForVaultState);
|
||||
this.anyVaultSelected = vault.isNotNull();
|
||||
}
|
||||
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
};
|
||||
if (state != null) {
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
};
|
||||
} else {
|
||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
+1
-1
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@@ -11,7 +12,6 @@ import org.cryptomator.keychain.KeychainManager;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
+11
-5
@@ -1,10 +1,10 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -18,14 +18,20 @@ public class VaultDetailUnknownErrorController implements FxController {
|
||||
|
||||
@Inject
|
||||
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault) {
|
||||
this.stackTrace = EasyBind.select(vault).selectObject(Vault::lastKnownExceptionProperty).map(this::provideStackTrace).orElse("");
|
||||
this.stackTrace = EasyBind.select(vault) //
|
||||
.selectObject(Vault::lastKnownExceptionProperty) //
|
||||
.map(this::provideStackTrace);
|
||||
}
|
||||
|
||||
private String provideStackTrace(Throwable cause) {
|
||||
// TODO deduplicate ErrorModule.java
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
cause.printStackTrace(new PrintStream(baos));
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
if (cause != null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
cause.printStackTrace(new PrintStream(baos));
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
@@ -7,7 +8,6 @@ import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -19,16 +19,22 @@ public class VaultListCellController implements FxController {
|
||||
|
||||
@Inject
|
||||
VaultListCellController() {
|
||||
this.glyph = EasyBind.select(vault).selectObject(Vault::stateProperty).map(this::getGlyphForVaultState).orElse(FontAwesome5Icon.EXCLAMATION_TRIANGLE);
|
||||
this.glyph = EasyBind.select(vault) //
|
||||
.selectObject(Vault::stateProperty) //
|
||||
.map(this::getGlyphForVaultState);
|
||||
}
|
||||
|
||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
};
|
||||
if(state != null){
|
||||
return switch (state) {
|
||||
case LOCKED -> FontAwesome5Icon.LOCK;
|
||||
case PROCESSING -> FontAwesome5Icon.SPINNER;
|
||||
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
|
||||
case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
};
|
||||
} else {
|
||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
@@ -18,7 +17,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListController implements FxController {
|
||||
@@ -43,7 +41,7 @@ public class VaultListController implements FxController {
|
||||
this.removeVault = removeVault;
|
||||
this.noVaultSelected = selectedVault.isNull();
|
||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||
selectedVault.addListener(this::selectedVaultDidChange);
|
||||
EasyBind.subscribe(selectedVault, this::selectedVaultDidChange);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -60,11 +58,10 @@ public class VaultListController implements FxController {
|
||||
});
|
||||
}
|
||||
|
||||
private void selectedVaultDidChange(@SuppressWarnings("unused") ObservableValue<? extends Vault> observableValue, @SuppressWarnings("unused") Vault oldValue, Vault newValue) {
|
||||
if (newValue == null) {
|
||||
return;
|
||||
private void selectedVaultDidChange(Vault newValue) {
|
||||
if (newValue != null) {
|
||||
VaultListManager.redetermineVaultState(newValue);
|
||||
}
|
||||
VaultListManager.redetermineVaultState(newValue);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -116,10 +116,10 @@ public class MigrationRunController implements FxController {
|
||||
return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME);
|
||||
}).onSuccess(needsAnotherMigration -> {
|
||||
if (needsAnotherMigration) {
|
||||
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayableName());
|
||||
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayName());
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
} else {
|
||||
LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
||||
LOG.info("Migration of '{}' succeeded.", vault.getDisplayName());
|
||||
vault.setState(VaultState.LOCKED);
|
||||
passwordField.wipe();
|
||||
window.setScene(successScene.get());
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.preferences;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.jni.MacFunctions;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -12,7 +13,7 @@ abstract class AutoStartModule {
|
||||
|
||||
@Provides
|
||||
@PreferencesScoped
|
||||
public static Optional<AutoStartStrategy> provideAutoStartStrategy(Optional<MacFunctions> macFunctions) {
|
||||
public static Optional<AutoStartStrategy> provideAutoStartStrategy(Optional<MacFunctions> macFunctions, Environment env) {
|
||||
if (SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent()) {
|
||||
return Optional.of(new AutoStartMacStrategy(macFunctions.get()));
|
||||
} else if (SystemUtils.IS_OS_WINDOWS) {
|
||||
|
||||
@@ -4,76 +4,185 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* OS specific class to check, en- and disable the auto start on Windows.
|
||||
* <p>
|
||||
* Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder.
|
||||
* <p>
|
||||
* The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator.
|
||||
* The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}.
|
||||
* <p>
|
||||
* To check if the feature is active, both strategies are applied.
|
||||
* To enable the feature, first the registry is tried and only on failure the autostart folder is used.
|
||||
* To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed.
|
||||
*
|
||||
* @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads.
|
||||
*/
|
||||
class AutoStartWinStrategy implements AutoStartStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class);
|
||||
private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\"";
|
||||
private static final String AUTOSTART_VALUE = "Cryptomator";
|
||||
private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk";
|
||||
|
||||
private final String exePath;
|
||||
|
||||
private boolean activatedUsingFolder;
|
||||
private boolean activatedUsingRegistry;
|
||||
|
||||
public AutoStartWinStrategy(String exePath) {
|
||||
this.exePath = exePath;
|
||||
this.activatedUsingFolder = false;
|
||||
this.activatedUsingRegistry = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Boolean> isAutoStartEnabled() {
|
||||
return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> isAutoStartEnabledUsingFolder() {
|
||||
Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY);
|
||||
this.activatedUsingFolder = Files.exists(autoStartEntry);
|
||||
return CompletableFuture.completedFuture(activatedUsingFolder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> isAutoStartEnabledUsingRegistry() {
|
||||
ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, //
|
||||
"/v", AUTOSTART_VALUE);
|
||||
try {
|
||||
Process proc = regQuery.start();
|
||||
return proc.onExit().thenApply(p -> p.exitValue() == 0);
|
||||
return proc.onExit().thenApply(p -> {
|
||||
this.activatedUsingRegistry = p.exitValue() == 0;
|
||||
return activatedUsingRegistry;
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
|
||||
LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableAutoStart() throws TogglingAutoStartFailedException {
|
||||
try {
|
||||
enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> {
|
||||
LOG.debug("Falling back to using autostart folder.");
|
||||
return this.enableAutoStartUsingFolder();
|
||||
}).thenAccept((Void v) -> this.activatedUsingFolder = true).get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted.");
|
||||
} catch (ExecutionException e) {
|
||||
throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder.");
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> enableAutoStartUsingRegistry() {
|
||||
ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, //
|
||||
"/v", AUTOSTART_VALUE, //
|
||||
"/t", "REG_SZ", //
|
||||
"/d", "\"" + exePath + "\"", //
|
||||
"/f");
|
||||
String command = regAdd.command().stream().collect(Collectors.joining(" "));
|
||||
try {
|
||||
Process proc = regAdd.start();
|
||||
boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS);
|
||||
if (finishedInTime) {
|
||||
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
|
||||
if (finishedInTime && proc.exitValue() == 0) {
|
||||
LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
throw new TogglingAutoStartFailedException("Adding registry value failed.");
|
||||
throw new IOException("Process exited with error code " + proc.exitValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new TogglingAutoStartFailedException("Adding registry value failed. " + command, e);
|
||||
LOG.debug("Registry could not be edited to set auto start.", e);
|
||||
return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed."));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> enableAutoStartUsingFolder() {
|
||||
String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY;
|
||||
String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();";
|
||||
ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand);
|
||||
try {
|
||||
Process proc = shortcutAdd.start();
|
||||
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
|
||||
if (finishedInTime && proc.exitValue() == 0) {
|
||||
LOG.debug("Created file {} for auto start.", autoStartFolderEntry);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
throw new IOException("Process exited with error code " + proc.exitValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Adding entry to auto start folder failed.", e);
|
||||
return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void disableAutoStart() throws TogglingAutoStartFailedException {
|
||||
ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, //
|
||||
"/v", AUTOSTART_VALUE, //
|
||||
"/f");
|
||||
String command = regRemove.command().stream().collect(Collectors.joining(" "));
|
||||
try {
|
||||
Process proc = regRemove.start();
|
||||
boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS);
|
||||
if (finishedInTime) {
|
||||
LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
|
||||
} else {
|
||||
throw new TogglingAutoStartFailedException("Removing registry value failed.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new TogglingAutoStartFailedException("Removing registry value failed. " + command, e);
|
||||
if (activatedUsingRegistry) {
|
||||
disableAutoStartUsingRegistry().whenComplete((voit, ex) -> {
|
||||
if (ex == null) {
|
||||
this.activatedUsingRegistry = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (activatedUsingFolder) {
|
||||
disableAutoStartUsingFolder().whenComplete((voit, ex) -> {
|
||||
if (ex == null) {
|
||||
this.activatedUsingFolder = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (activatedUsingRegistry || activatedUsingFolder) {
|
||||
throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder.");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean waitForProcess(Process proc, int timeout, TimeUnit timeUnit) {
|
||||
public CompletableFuture<Void> disableAutoStartUsingRegistry() {
|
||||
ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, //
|
||||
"/v", AUTOSTART_VALUE, //
|
||||
"/f");
|
||||
try {
|
||||
Process proc = regRemove.start();
|
||||
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
|
||||
if (finishedInTime && proc.exitValue() == 0) {
|
||||
LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
throw new IOException("Process exited with error code " + proc.exitValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Registry could not be edited to remove auto start.", e);
|
||||
return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed."));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> disableAutoStartUsingFolder() {
|
||||
try {
|
||||
Files.delete(Path.of(WINDOWS_START_MENU_ENTRY));
|
||||
LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (NoSuchFileException e) {
|
||||
//that is also okay
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Failed to delete entry from auto start folder.", e);
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) {
|
||||
boolean finishedInTime = false;
|
||||
try {
|
||||
finishedInTime = proc.waitFor(timeout, timeUnit);
|
||||
@@ -88,4 +197,11 @@ class AutoStartWinStrategy implements AutoStartStrategy {
|
||||
return finishedInTime;
|
||||
}
|
||||
|
||||
private class SystemCommandException extends RuntimeException {
|
||||
|
||||
public SystemCommandException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class QuitController implements FxController {
|
||||
|
||||
Task<Collection<Vault>> lockAllTask = vaultService.createLockAllTask(unlockedVaults, false);
|
||||
lockAllTask.setOnSucceeded(evt -> {
|
||||
LOG.info("Locked {}", lockAllTask.getValue().stream().map(Vault::getDisplayableName).collect(Collectors.joining(", ")));
|
||||
LOG.info("Locked {}", lockAllTask.getValue().stream().map(Vault::getDisplayName).collect(Collectors.joining(", ")));
|
||||
if (unlockedVaults.isEmpty()) {
|
||||
window.close();
|
||||
response.performQuit();
|
||||
|
||||
@@ -9,7 +9,6 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -25,7 +24,6 @@ import org.cryptomator.ui.common.StageFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@@ -107,7 +105,7 @@ abstract class RecoveryKeyModule {
|
||||
@IntoMap
|
||||
@FxControllerKey(RecoveryKeyDisplayController.class)
|
||||
static FxController provideRecoveryKeyDisplayController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, ResourceBundle localization) {
|
||||
return new RecoveryKeyDisplayController(window, vault.getDisplayableName(), recoveryKey.get(), localization);
|
||||
return new RecoveryKeyDisplayController(window, vault.getDisplayName(), recoveryKey.get(), localization);
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RemoveVaultController implements FxController {
|
||||
@FXML
|
||||
public void finish() {
|
||||
vaults.remove(vault);
|
||||
LOG.debug("Removing vault {}.", vault.getDisplayableName());
|
||||
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class TrayMenuController {
|
||||
}
|
||||
|
||||
private Menu buildSubmenu(Vault vault) {
|
||||
Menu submenu = new Menu(vault.getDisplayableName());
|
||||
Menu submenu = new Menu(vault.getDisplayName());
|
||||
|
||||
if (vault.isLocked()) {
|
||||
MenuItem unlockItem = new MenuItem(resourceBundle.getString("traymenu.vault.unlock"));
|
||||
|
||||
@@ -77,7 +77,7 @@ public class UnlockController implements FxController {
|
||||
this.unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, passwordEntryLock.awaitingInteraction());
|
||||
this.userInteractionDisabled = passwordEntryLock.awaitingInteraction().not();
|
||||
this.unlockButtonDisabled = new SimpleBooleanProperty();
|
||||
this.vaultName = WeakBindings.bindString(vault.displayableNameProperty());
|
||||
this.vaultName = WeakBindings.bindString(vault.displayNameProperty());
|
||||
this.window.setOnCloseRequest(windowEvent -> cancel());
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ abstract class UnlockModule {
|
||||
@UnlockScoped
|
||||
static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Named("unlockWindowOwner") Optional<Stage> owner) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(vault.getDisplayableName());
|
||||
stage.setTitle(vault.getDisplayName());
|
||||
stage.setResizable(false);
|
||||
if (owner.isPresent()) {
|
||||
stage.initOwner(owner.get());
|
||||
|
||||
@@ -129,7 +129,7 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
}
|
||||
|
||||
private void handleSuccess() {
|
||||
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName());
|
||||
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
|
||||
if (savePassword.get()) {
|
||||
savePasswordToSystemkeychain();
|
||||
}
|
||||
|
||||
+3
@@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.util.StringConverter;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.common.settings.WhenUnlocked;
|
||||
@@ -18,6 +19,7 @@ public class GeneralVaultOptionsController implements FxController {
|
||||
private final Vault vault;
|
||||
private final ResourceBundle resourceBundle;
|
||||
|
||||
public TextField vaultName;
|
||||
public CheckBox unlockOnStartupCheckbox;
|
||||
public ChoiceBox<WhenUnlocked> actionAfterUnlockChoiceBox;
|
||||
|
||||
@@ -29,6 +31,7 @@ public class GeneralVaultOptionsController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
vaultName.textProperty().bindBidirectional(vault.getVaultSettings().displayName());
|
||||
unlockOnStartupCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().unlockAfterStartup());
|
||||
actionAfterUnlockChoiceBox.getItems().addAll(WhenUnlocked.values());
|
||||
actionAfterUnlockChoiceBox.valueProperty().bindBidirectional(vault.getVaultSettings().actionAfterUnlock());
|
||||
|
||||
@@ -43,7 +43,6 @@ public class MountOptionsController implements FxController {
|
||||
private final BooleanBinding webDavAndWindows;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
private final ResourceBundle resourceBundle;
|
||||
public TextField driveName;
|
||||
public CheckBox readOnlyCheckbox;
|
||||
public CheckBox customMountFlagsCheckbox;
|
||||
public TextField mountFlags;
|
||||
@@ -70,7 +69,6 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
driveName.textProperty().bindBidirectional(vault.getVaultSettings().mountName());
|
||||
|
||||
// readonly:
|
||||
readOnlyCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().usesReadOnlyMode());
|
||||
|
||||
@@ -5,7 +5,6 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -20,9 +19,7 @@ import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@@ -41,7 +38,7 @@ abstract class VaultOptionsModule {
|
||||
@VaultOptionsScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, @VaultOptionsWindow Vault vault) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(vault.getDisplayableName());
|
||||
stage.setTitle(vault.getDisplayName());
|
||||
stage.setResizable(true);
|
||||
stage.setMinWidth(400);
|
||||
stage.setMinHeight(300);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<FormattedLabel format="%addvaultwizard.success.nextStepsInstructions" arg1="${controller.vault.displayableName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
<FormattedLabel format="%addvaultwizard.success.nextStepsInstructions" arg1="${controller.vault.displayName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</padding>
|
||||
<children>
|
||||
<VBox spacing="6">
|
||||
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayName}" wrapText="true"/>
|
||||
<NiceSecurePasswordField fx:id="oldPasswordField"/>
|
||||
</VBox>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</padding>
|
||||
<children>
|
||||
<VBox spacing="6" visible="${!controller.vault.processing}" managed="${!controller.vault.processing}">
|
||||
<FormattedLabel format="%migration.run.enterPassword" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||
<FormattedLabel format="%migration.run.enterPassword" arg1="${controller.vault.displayName}" wrapText="true"/>
|
||||
<NiceSecurePasswordField fx:id="passwordField"/>
|
||||
</VBox>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</StackPane>
|
||||
|
||||
<VBox spacing="6" HBox.hgrow="ALWAYS">
|
||||
<FormattedLabel format="%migration.start.prompt" arg1="${controller.vault.displayableName}" wrapText="true" />
|
||||
<FormattedLabel format="%migration.start.prompt" arg1="${controller.vault.displayName}" wrapText="true" />
|
||||
<CheckBox fx:id="confirmSyncDone" text="%migration.start.confirm"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="24"/>
|
||||
</StackPane>
|
||||
<FormattedLabel format="%migration.success.nextStepsInstructions" arg1="${controller.vault.displayableName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
<FormattedLabel format="%migration.success.nextStepsInstructions" arg1="${controller.vault.displayName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
|
||||
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
spacing="6"
|
||||
alignment="CENTER_LEFT">
|
||||
<fx:define>
|
||||
<FontAwesome5IconView fx:id="checkmark" styleClass="glyph-icon-primary" glyph="CHECK"/>
|
||||
<FontAwesome5IconView fx:id="warning" styleClass="glyph-icon-orange" glyph="EXCLAMATION_TRIANGLE"/>
|
||||
<FontAwesome5IconView fx:id="cross" styleClass="glyph-icon-red" glyph="TIMES"/>
|
||||
<FontAwesome5IconView fx:id="passwordStrengthCheckmark" styleClass="glyph-icon-primary" glyph="CHECK"/>
|
||||
<FontAwesome5IconView fx:id="passwordStrengthWarning" styleClass="glyph-icon-orange" glyph="EXCLAMATION_TRIANGLE"/>
|
||||
<FontAwesome5IconView fx:id="passwordStrengthCross" styleClass="glyph-icon-red" glyph="TIMES"/>
|
||||
<FontAwesome5IconView fx:id="passwordMatchCheckmark" styleClass="glyph-icon-primary" glyph="CHECK"/>
|
||||
<FontAwesome5IconView fx:id="passwordMatchCross" styleClass="glyph-icon-red" glyph="TIMES"/>
|
||||
</fx:define>
|
||||
<children>
|
||||
<Label text="%newPassword.promptText" labelFor="$passwordField"/>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</padding>
|
||||
<children>
|
||||
<VBox spacing="6">
|
||||
<FormattedLabel format="%recoveryKey.enterPassword.prompt" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||
<FormattedLabel format="%recoveryKey.enterPassword.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
|
||||
<NiceSecurePasswordField fx:id="passwordField" HBox.hgrow="ALWAYS"/>
|
||||
</VBox>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
|
||||
|
||||
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="24"/>
|
||||
</StackPane>
|
||||
<VBox spacing="6">
|
||||
<FormattedLabel format="%unlock.success.message" arg1="${controller.vault.displayableName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
<FormattedLabel format="%unlock.success.message" arg1="${controller.vault.displayName}" wrapText="true" HBox.hgrow="ALWAYS"/>
|
||||
<CheckBox text="%unlock.success.rememberChoice" fx:id="rememberChoiceCheckbox"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
</StackPane>
|
||||
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
||||
<HBox spacing="12">
|
||||
<Label styleClass="label-large" text="${controller.vault.displayableName}">
|
||||
<Label styleClass="label-large" text="${controller.vault.displayName}">
|
||||
<tooltip>
|
||||
<Tooltip text="${controller.vault.displayableName}"/>
|
||||
<Tooltip text="${controller.vault.displayName}"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<FontAwesome5IconView glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
|
||||
</VBox>
|
||||
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="header-label" text="${controller.vault.displayableName}"/>
|
||||
<Label styleClass="header-label" text="${controller.vault.displayName}"/>
|
||||
<Label styleClass="detail-label" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS"/>
|
||||
</VBox>
|
||||
</children>
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.cryptomator.ui.vaultoptions.GeneralVaultOptionsController"
|
||||
@@ -14,6 +15,11 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<Label text="%vaultOptions.general.vaultName"/>
|
||||
<TextField fx:id="vaultName"/>
|
||||
</HBox>
|
||||
|
||||
<CheckBox text="%vaultOptions.general.unlockAfterStartup" fx:id="unlockOnStartupCheckbox"/>
|
||||
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
|
||||
@@ -23,11 +23,6 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<Label text="%vaultOptions.mount.driveName"/>
|
||||
<AlphanumericTextField fx:id="driveName"/>
|
||||
</HBox>
|
||||
|
||||
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly"/>
|
||||
|
||||
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags" visible="${!controller.webDavAndWindows}" managed="${!controller.webDavAndWindows}"/>
|
||||
|
||||
@@ -17,6 +17,9 @@ generic.button.print=Print
|
||||
generic.error.title=An unexpected error occured
|
||||
generic.error.instruction=This should not have happened. Please report the error text below and include a description of what steps did lead to this error.
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Vault
|
||||
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=Show
|
||||
traymenu.showPreferencesWindow=Preferences
|
||||
@@ -216,6 +219,7 @@ wrongFileAlert.link=For further assistance, visit
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general=General
|
||||
vaultOptions.general.vaultName=Vault Name
|
||||
vaultOptions.general.unlockAfterStartup=Unlock vault when starting Cryptomator
|
||||
vaultOptions.general.actionAfterUnlock=After successful unlock
|
||||
vaultOptions.general.actionAfterUnlock.ignore=Do nothing
|
||||
@@ -224,7 +228,6 @@ vaultOptions.general.actionAfterUnlock.ask=Ask
|
||||
## Mount
|
||||
vaultOptions.mount=Mounting
|
||||
vaultOptions.mount.readonly=Read-Only
|
||||
vaultOptions.mount.driveName=Drive Name
|
||||
vaultOptions.mount.customMountFlags=Custom Mount Flags
|
||||
vaultOptions.mount.winDriveLetterOccupied=occupied
|
||||
vaultOptions.mount.mountPoint=Mount Point
|
||||
|
||||
@@ -11,7 +11,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
Cryptomator uses 52 third-party dependencies under the following licenses:
|
||||
Cryptomator uses 53 third-party dependencies under the following licenses:
|
||||
Apache License v2.0:
|
||||
- HKDF-RFC5869 (at.favre.lib:hkdf:1.1.0 - https://github.com/patrickfav/hkdf)
|
||||
- jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi)
|
||||
@@ -82,9 +82,10 @@ Cryptomator uses 52 third-party dependencies under the following licenses:
|
||||
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j)
|
||||
- secret-service (de.swiesend:secret-service:1.1.0 - https://github.com/swiesend/secret-service)
|
||||
- Checker Qual (org.checkerframework:checker-qual:2.11.1 - https://checkerframework.org)
|
||||
- kdewallet (org.purejava:kdewallet:1.0.1 - https://github.com/purejava/kdewallet)
|
||||
- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org)
|
||||
The BSD 2-Clause License:
|
||||
- EasyBind (org.fxmisc.easybind:easybind:1.0.3 - http://www.fxmisc.org/easybind/)
|
||||
- EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind)
|
||||
|
||||
Cryptomator uses other third-party assets under the following licenses:
|
||||
SIL OFL 1.1 License:
|
||||
|
||||
Reference in New Issue
Block a user