Merge branch 'feature/files-in-use' into feature/notify-fallback

# Conflicts:
#	src/main/java/org/cryptomator/common/EventMap.java
#	src/main/java/org/cryptomator/common/vaults/Vault.java
This commit is contained in:
Armin Schrenk
2025-12-12 12:31:08 +01:00
37 changed files with 237 additions and 102 deletions

View File

@@ -7,6 +7,16 @@ on:
description: "Url to the file to upload"
required: true
type: string
avast:
description: "Upload to Avast"
required: false
type: boolean
default: true
kaspersky:
description: "Upload to Kaspersky"
required: false
type: boolean
default: true
workflow_dispatch:
inputs:
url:
@@ -39,7 +49,7 @@ jobs:
url="${INPUT_URL}"
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
- name: Download file
run: curl --remote-name ${INPUT_URL} -L -o ${{steps.extractName.outputs.fileName}}
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
@@ -50,7 +60,7 @@ jobs:
name: Anti Virus Allowlisting Kaspersky
runs-on: ubuntu-latest
needs: download-file
if: github.event_name == 'workflow_call' || inputs.kaspersky
if: inputs.kaspersky
steps:
- name: Download artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
@@ -70,7 +80,7 @@ jobs:
name: Anti Virus Allowlisting Avast
runs-on: ubuntu-latest
needs: download-file
if: github.event_name == 'workflow_call' || inputs.avast
if: inputs.avast
steps:
- name: Download artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0

View File

@@ -33,7 +33,7 @@
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.10.0-beta2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta1</cryptomator.integrations.mac.version>

View File

@@ -0,0 +1,16 @@
package org.cryptomator.common;
/**
* Objects which has some kind of owner.
*/
@FunctionalInterface
public interface FilsystemOwnerSupplier {
/**
* Get the object owner.
*
* @return the object owner
*/
String getOwner();
}

View File

@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.common.FilsystemOwnerSupplier;
import org.cryptomator.common.mount.Mounter;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
@@ -149,14 +150,17 @@ public class Vault {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
}
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.withFilesystemEventConsumer(this::consumeVaultEvent) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
.withFilesystemEventConsumer(this::consumeVaultEvent);
if (keyLoader instanceof FilsystemOwnerSupplier oo) {
fsPropsBuilder.withOwnerGetter(oo::getOwner);
}
return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build());
}
private void destroyCryptoFileSystem() {

View File

@@ -6,6 +6,7 @@ import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import javax.inject.Inject;
@@ -101,6 +102,7 @@ public class FileSystemEventAggregator {
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext;
};
return new FSEventBucket(v, p, event.getClass());
}

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
@@ -115,7 +116,7 @@ public class EventListCellController implements FxController {
eventActionsMenu.hide();
eventActionsMenu.getItems().clear();
eventTooltip.setText(item.getKey().vault().getDisplayName());
addAction("generic.action.dismiss", () -> {
addLocalizedAction("generic.action.dismiss", () -> {
fileSystemEventAggregator.remove(item.getKey());
});
switch (item.getValue().mostRecentEvent()) {
@@ -124,20 +125,36 @@ public class EventListCellController implements FxController {
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
case FileIsInUseEvent fse -> this.adjustToFileInUseEvent(fse);
}
}
private void adjustToFileInUseEvent(FileIsInUseEvent fse) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue("File is in use by " + fse.owner());
var indexFileName = fse.cleartextPath().lastIndexOf("/");
eventDescription.setValue(fse.cleartextPath().substring(indexFileName + 1));
if (revealService != null) {
addAction("Show decrypted file", () -> reveal(revealService, convertVaultPathToSystemPath(fse.cleartextPath())));
addAction("Show encrypted Path", () -> reveal(revealService, fse.ciphertextPath()));
} else {
addAction("Copy decrypted Path", () -> copyToClipboard(convertVaultPathToSystemPath(fse.cleartextPath()).toString()));
addAction("Copy encrypted Path", () -> copyToClipboard(fse.ciphertextPath().toString()));
}
addAction("Ignore in use status ", fse.ignoreMethod());
}
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
} else {
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
}
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
}
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
@@ -145,9 +162,9 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else {
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
}
}
@@ -156,11 +173,11 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
} else {
addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
}
}
@@ -169,9 +186,9 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else {
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
}
}
@@ -180,14 +197,19 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else {
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
}
}
private void addAction(String localizationKey, Runnable action) {
var entry = new MenuItem(resourceBundle.getString(localizationKey));
private void addLocalizedAction(String localizationKey, Runnable action) {
var entryText = resourceBundle.getString(localizationKey);
addAction(entryText, action);
}
private void addAction(String entryText, Runnable action) {
var entry = new MenuItem(entryText);
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
entry.setOnAction(_ -> action.run());
eventActionsMenu.getItems().addLast(entry);
@@ -234,18 +256,14 @@ public class EventListCellController implements FxController {
}
}
private Path convertVaultPathToSystemPath(Path p) {
if (!(p instanceof CryptoPath)) {
throw new IllegalArgumentException("Path " + p + " is not a vault path");
}
private Path convertVaultPathToSystemPath(String vaultInternalPath) {
var v = eventEntry.getValue().getKey().vault();
if (!v.isUnlocked()) {
return Path.of(System.getProperty("user.home"));
}
var mountUri = v.getMountPoint().uri();
var internalPath = p.toString().substring(1);
return Path.of(mountUri.getPath().concat(internalPath).substring(1));
return Path.of(mountUri.getPath().concat(vaultInternalPath.substring(1)).substring(1));
}
private void reveal(RevealPathService s, Path p) {

View File

@@ -63,8 +63,8 @@ abstract class HealthCheckModule {
@Provides
@HealthCheckWindow
@HealthCheckScoped
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
return compFactory.create(vault, window).keyloadingStrategy();
}
@Provides

View File

@@ -3,11 +3,8 @@ package org.cryptomator.ui.keyloading;
import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import javafx.stage.Stage;
import java.util.Map;
import java.util.function.Supplier;
@KeyLoadingScoped
@Subcomponent(modules = {KeyLoadingModule.class})
@@ -16,16 +13,10 @@ public interface KeyLoadingComponent {
@KeyLoading
KeyLoadingStrategy keyloadingStrategy();
@Subcomponent.Builder
interface Builder {
@Subcomponent.Factory
interface Factory {
@BindsInstance
Builder vault(@KeyLoading Vault vault);
@BindsInstance
Builder window(@KeyLoading Stage window);
KeyLoadingComponent build();
KeyLoadingComponent create(@BindsInstance @KeyLoading Vault vault, @KeyLoading @BindsInstance Stage window);
}
}

View File

@@ -66,6 +66,13 @@ public abstract class HubKeyLoadingModule {
return new AtomicReference<>();
}
@Provides
@Named("userName")
@KeyLoadingScoped
static AtomicReference<String> provideUserNameRef() {
return new AtomicReference<>();
}
@Provides
@KeyLoadingScoped
static CompletableFuture<ReceivedKey> provideResult() {

View File

@@ -2,6 +2,7 @@ package org.cryptomator.ui.keyloading.hub;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.FilsystemOwnerSupplier;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
import org.cryptomator.common.settings.DeviceKey;
@@ -23,25 +24,28 @@ import java.net.URI;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
@KeyLoading
public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilsystemOwnerSupplier {
private static final String SCHEME_PREFIX = "hub+";
public static final String SCHEME_PREFIX = "hub+";
public static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http";
public static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https";
private final Stage window;
private final KeychainManager keychainManager;
private final AtomicReference<String> userName;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> noKeychainScene;
private final CompletableFuture<ReceivedKey> result;
private final DeviceKey deviceKey;
@Inject
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("userName") AtomicReference<String> userName) {
this.window = window;
this.keychainManager = keychainManager;
this.userName = userName;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.authFlowScene = authFlowScene;
@@ -90,4 +94,13 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
});
}
@Override
public String getOwner() {
var name = userName.get();
if (name == null) {
throw new IllegalStateException("Owner is not yet determined");
}
return name;
}
}

View File

@@ -41,7 +41,6 @@ import java.util.concurrent.atomic.AtomicReference;
public class ReceiveKeyController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
private static final String SCHEME_PREFIX = "hub+";
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
@@ -50,6 +49,7 @@ public class ReceiveKeyController implements FxController {
private final String vaultId;
private final String deviceId;
private final String bearerToken;
private final AtomicReference<String> userName;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> registerDeviceScene;
private final Lazy<Scene> legacyRegisterDeviceScene;
@@ -59,12 +59,25 @@ public class ReceiveKeyController implements FxController {
private final HttpClient httpClient;
@Inject
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
public ReceiveKeyController(@KeyLoading Vault vault, //
ExecutorService executor, //
@KeyLoading Stage window, //
HubConfig hubConfig, //
@Named("deviceId") String deviceId, //
@Named("bearerToken") AtomicReference<String> tokenRef, //
@Named("userName") AtomicReference<String> userName, //
CompletableFuture<ReceivedKey> result, //
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, //
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, //
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, //
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, //
@FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
this.window = window;
this.hubConfig = hubConfig;
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
this.deviceId = deviceId;
this.bearerToken = Objects.requireNonNull(tokenRef.get());
this.userName = userName;
this.result = result;
this.registerDeviceScene = registerDeviceScene;
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
@@ -81,7 +94,34 @@ public class ReceiveKeyController implements FxController {
}
public void receiveKey() {
requestApiConfig();
requestUserData();
}
private void requestUserData() {
var userUri = hubConfig.URIs.API.resolve("users/me?withDevices=false");
var request = HttpRequest.newBuilder(userUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
.thenAcceptAsync(this::receivedUserData) //
.exceptionally(this::retrievalFailed);
}
private void receivedUserData(HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
try {
if (response.statusCode() == 200) {
var user = JSON.reader().readValue(response.body(), UserDto.class);
userName.set(user.name);
requestApiConfig();
} else {
throw new IllegalStateException("Unexpected response " + response.statusCode());
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
@@ -289,11 +329,14 @@ public class ReceiveKeyController implements FxController {
}
private static String extractVaultId(URI vaultKeyUri) {
assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX);
assert vaultKeyUri.getScheme().startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX);
var path = vaultKeyUri.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
@JsonIgnoreProperties(ignoreUnknown = true)
private record UserDto(@JsonProperty(value = "name", required = true) String name) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}

View File

@@ -57,8 +57,8 @@ abstract class UnlockModule {
@Provides
@UnlockWindow
@UnlockScoped
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
return compFactory.create(vault, window).keyloadingStrategy();
}
@Provides

View File

@@ -39,9 +39,9 @@
<CheckBox fx:id="expertSettingsCheckBox" text="%addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox" onAction="#toggleUseExpertSettings"/>
<VBox spacing="6" visible="${expertSettingsCheckBox.selected}">
<HBox spacing="2" HBox.hgrow="NEVER">
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title"/>
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title" labelFor="$shorteningThresholdTextField"/>
<Region prefWidth="2"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" accessibleText="%addvaultwizard.new.expertSettings.shorteningThreshold.tooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>

View File

@@ -19,7 +19,7 @@
</padding>
<children>
<VBox spacing="6">
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayName}" wrapText="true"/>
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayName}" wrapText="true" labelFor="$oldPasswordField"/>
<NiceSecurePasswordField fx:id="oldPasswordField"/>
</VBox>

View File

@@ -22,7 +22,7 @@
<Insets left="6"/>
</padding>
<Region HBox.hgrow="ALWAYS"/>
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#copyTableToClipboard">
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#copyTableToClipboard" accessibleText="%decryptNames.copyTable.tooltip">
<graphic>
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="16"/>
</graphic>
@@ -30,7 +30,7 @@
<Tooltip text="%decryptNames.copyTable.tooltip"/>
</tooltip>
</Button>
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#clearTable">
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#clearTable" accessibleText="%decryptNames.clearTable.tooltip">
<graphic>
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
</graphic>
@@ -48,12 +48,12 @@
</Button>
</placeholder>
<columns>
<TableColumn fx:id="ciphertextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}">
<TableColumn fx:id="ciphertextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}" text="%decryptNames.column.encrypted">
<graphic>
<FontAwesome5IconView glyph="LOCK"/>
</graphic>
</TableColumn>
<TableColumn fx:id="cleartextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}">
<TableColumn fx:id="cleartextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}" text="%decryptNames.column.decrypted">
<graphic>
<FontAwesome5IconView glyph="LOCK_OPEN"/>
</graphic>

View File

@@ -79,7 +79,7 @@
<padding>
<Insets top="6" bottom="6"/>
</padding>
<Label text="%error.technicalDetails"/>
<Label text="%error.technicalDetails" labelFor="$detailsTextArea"/>
<Region HBox.hgrow="ALWAYS"/>
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
<graphic>
@@ -92,7 +92,7 @@
</graphic>
</Hyperlink>
</HBox>
<TextArea VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
<TextArea fx:id="detailsTextArea" VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
<Region minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">

View File

@@ -20,9 +20,9 @@
<padding>
<Insets left="6" />
</padding>
<ChoiceBox fx:id="vaultFilterChoiceBox" minWidth="42"/>
<ChoiceBox fx:id="vaultFilterChoiceBox" minWidth="42" accessibleText="%eventView.filterVaults"/>
<Region HBox.hgrow="ALWAYS"/>
<Button styleClass="button-right" onAction="#clearEvents" contentDisplay="GRAPHIC_ONLY">
<Button styleClass="button-right" onAction="#clearEvents" contentDisplay="GRAPHIC_ONLY" accessibleText="%eventView.clearListButton.tooltip">
<graphic>
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
</graphic>

View File

@@ -5,6 +5,7 @@
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<HBox xmlns:fx="http://javafx.com/fxml"
@@ -29,10 +30,13 @@
</HBox>
<Label text="${controller.description}"/>
</VBox>
<Button fx:id="eventActionsButton" contentDisplay="GRAPHIC_ONLY" onAction="#toggleEventActionsMenu" managed="${controller.actionsButtonVisible}" visible="${controller.actionsButtonVisible}">
<Button fx:id="eventActionsButton" contentDisplay="GRAPHIC_ONLY" onAction="#toggleEventActionsMenu" managed="${controller.actionsButtonVisible}" visible="${controller.actionsButtonVisible}" accessibleText="%eventView.cell.actionsButton.tooltip">
<graphic>
<FontAwesome5IconView glyph="ELLIPSIS_V" glyphSize="16"/>
</graphic>
<tooltip>
<Tooltip text="%eventView.cell.actionsButton.tooltip"/>
</tooltip>
</Button>
<VBox alignment="CENTER" maxWidth="64" minWidth="64" visible="${!controller.actionsButtonVisible}" managed="${!controller.actionsButtonVisible}">
<Label text="${controller.eventLocalTime}" />

View File

@@ -47,8 +47,8 @@
<FontAwesome5IconView glyph="FUNNEL" glyphSize="${filterLbl.height}" styleClass="glyph-icon-muted"/>
</graphic>
</Label>
<ChoiceBox fx:id="severityChoiceBox" />
<ChoiceBox fx:id="fixStateChoiceBox" />
<ChoiceBox fx:id="severityChoiceBox" accessibleText="%health.check.detail.filterSeverity"/>
<ChoiceBox fx:id="fixStateChoiceBox" accessibleText="%health.check.detail.filterFixState"/>
</HBox>
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS" visible="${!controller.checkSkipped}" fixedCellSize="25">
<contextMenu>

View File

@@ -21,6 +21,6 @@
<CheckBox fx:id="checkbox" visible="${controller.checkRunnable}"/>
<CheckStateIconView check="${controller.check}" glyphSize="20" visible="${!controller.checkRunnable}"/>
</StackPane>
<Label text="${controller.checkName}"/>
<Label text="${controller.checkName}" labelFor="$checkbox"/>
</HBox>

View File

@@ -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.displayName}" wrapText="true"/>
<FormattedLabel format="%migration.run.enterPassword" arg1="${controller.vault.displayName}" wrapText="true" labelFor="$passwordField"/>
<NiceSecurePasswordField fx:id="passwordField"/>
</VBox>

View File

@@ -26,6 +26,6 @@
</VBox>
</HBox>
<TextArea text="${controller.thirdPartyLicenseText}" editable="false"/>
<TextArea text="${controller.thirdPartyLicenseText}" editable="false" accessibleText="%preferences.about.thirdPartyLicenses"/>
</children>
</VBox>

View File

@@ -7,6 +7,7 @@
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
@@ -32,10 +33,13 @@
<FormattedLabel format="%preferences.contribute.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
<Region minHeight="12"/>
<HBox alignment="BOTTOM_CENTER" spacing="6">
<Button onAction="#didClickRemoveCert">
<Button onAction="#didClickRemoveCert" contentDisplay="GRAPHIC_ONLY" accessibleText="%preferences.contribute.removeCert.tooltip">
<graphic>
<FontAwesome5IconView glyph="TRASH"/>
</graphic>
<tooltip>
<Tooltip text="%preferences.contribute.removeCert.tooltip"/>
</tooltip>
</Button>
<Button text="%preferences.contribute.donate" minWidth="100" onAction="#showDonate">
<graphic>
@@ -56,7 +60,7 @@
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="HAND_HOLDING_HEART" glyphSize="24"/>
</StackPane>
<VBox HBox.hgrow="ALWAYS" spacing="6">
<Label text="%preferences.contribute.noCertificate" wrapText="true" VBox.vgrow="ALWAYS"/>
<Label text="%preferences.contribute.noCertificate" wrapText="true" VBox.vgrow="ALWAYS" labelFor="$supporterCertificateField"/>
<Hyperlink text="%preferences.contribute.getCertificate" onAction="#getSupporterCertificate">
<graphic>
<FontAwesome5IconView glyph="LINK"/>

View File

@@ -27,12 +27,12 @@
<HBox spacing="12" alignment="CENTER_LEFT">
<CheckBox fx:id="useKeychainCheckbox" text="%preferences.general.keychainBackend"/>
<ChoiceBox fx:id="keychainBackendChoiceBox"/>
<ChoiceBox fx:id="keychainBackendChoiceBox" accessibleText="%preferences.general.keychainBackend"/>
</HBox>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.someQuickAccessServiceAvailable}" managed="${controller.someQuickAccessServiceAvailable}">
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
<ChoiceBox fx:id="quickAccessServiceChoiceBox"/>
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
</HBox>
<Region VBox.vgrow="ALWAYS"/>

View File

@@ -21,13 +21,13 @@
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%preferences.interface.theme"/>
<Label text="%preferences.interface.theme" labelFor="$themeChoiceBox"/>
<ChoiceBox fx:id="themeChoiceBox" disable="${!controller.licenseHolder.validLicense}"/>
<Hyperlink styleClass="hyperlink-underline,hyperlink-muted" text="%preferences.interface.unlockThemes" onAction="#showContributeTab" visible="${!controller.licenseHolder.validLicense}" managed="${!controller.licenseHolder.validLicense}"/>
</HBox>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%preferences.interface.language"/>
<Label text="%preferences.interface.language" labelFor="$preferredLanguageChoiceBox"/>
<ChoiceBox fx:id="preferredLanguageChoiceBox"/>
</HBox>

View File

@@ -20,9 +20,9 @@
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%preferences.volume.type"/>
<Label text="%preferences.volume.type" labelFor="$volumeTypeChoiceBox"/>
<ChoiceBox fx:id="volumeTypeChoiceBox"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" accessibleText="%preferences.volume.docsTooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
@@ -33,7 +33,7 @@
</HBox>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}">
<Label text="%preferences.volume.tcp.port"/>
<Label text="%preferences.volume.tcp.port" labelFor="$loopbackPortField"/>
<NumericTextField fx:id="loopbackPortField"/>
<Button text="%generic.button.apply" fx:id="loopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
</HBox>

View File

@@ -40,7 +40,7 @@
<Insets bottom="6" top="6"/>
</padding>
</Label>
<FormattedLabel fx:id="descriptionLabel" format="%recoveryKey.create.description" arg1="${controller.vault.displayName}" wrapText="true">
<FormattedLabel fx:id="descriptionLabel" format="%recoveryKey.create.description" arg1="${controller.vault.displayName}" wrapText="true" labelFor="$passwordField">
<padding>
<Insets bottom="6"/>
</padding>

View File

@@ -16,7 +16,7 @@
spacing="12"
alignment="TOP_LEFT">
<children>
<FormattedLabel format="%recoveryKey.display.description" arg1="${controller.vaultName}" wrapText="true"/>
<FormattedLabel format="%recoveryKey.display.description" arg1="${controller.vaultName}" wrapText="true" labelFor="$textarea"/>
<TextArea editable="false" text="${controller.recoveryKey}" wrapText="true" prefRowCount="4" fx:id="textarea"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+R">

View File

@@ -48,7 +48,7 @@
<HBox spacing="2" HBox.hgrow="NEVER">
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title"/>
<Region prefWidth="2"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" accessibleText="%addvaultwizard.new.expertSettings.shorteningThreshold.tooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
@@ -57,7 +57,7 @@
</tooltip>
</Hyperlink>
</HBox>
<Label text="%recover.expertSettings.shorteningThreshold.title" wrapText="true"/>
<Label text="%recover.expertSettings.shorteningThreshold.title" wrapText="true" labelFor="$shorteningThresholdTextField"/>
<NumericTextField fx:id="shorteningThresholdTextField"/>
<HBox alignment="TOP_RIGHT">
<Region minWidth="4" prefWidth="4" HBox.hgrow="NEVER"/>

View File

@@ -12,7 +12,7 @@
minHeight="145"
spacing="12">
<children>
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true" labelFor="$textarea"/>
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/>
<VBox>

View File

@@ -68,7 +68,7 @@
<TextFlow styleClass="text-flow">
<Text text="%shareVault.remarkBestPractices"/>
<Text text=" "/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#visitBestPractices">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#visitBestPractices" accessibleText="%shareVault.docsTooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>

View File

@@ -46,7 +46,7 @@
</ImageView>
</StackPane>
<VBox spacing="6" HBox.hgrow="ALWAYS">
<FormattedLabel format="%unlock.passwordPrompt" arg1="${controller.vaultName}" wrapText="true"/>
<FormattedLabel format="%unlock.passwordPrompt" arg1="${controller.vaultName}" wrapText="true" labelFor="$passwordField"/>
<NiceSecurePasswordField fx:id="passwordField" disable="${controller.userInteractionDisabled}"/>
<CheckBox fx:id="savePasswordCheckbox" text="%unlock.savePassword" onAction="#didClickSavePasswordCheckbox" disable="${controller.userInteractionDisabled}" visible="${controller.keychainAccessAvailable}"/>
</VBox>

View File

@@ -15,7 +15,7 @@
alignment="TOP_CENTER"
spacing="9">
<Label text="%main.vaultDetail.accessLocation"/>
<Button styleClass="button-large" contentDisplay="GRAPHIC_ONLY" minWidth="120" onAction="#revealAccessLocation" defaultButton="${controller.accessibleViaPath}" visible="${controller.accessibleViaPath}" managed="${controller.accessibleViaPath}">
<Button styleClass="button-large" contentDisplay="GRAPHIC_ONLY" minWidth="120" onAction="#revealAccessLocation" defaultButton="${controller.accessibleViaPath}" visible="${controller.accessibleViaPath}" managed="${controller.accessibleViaPath}" accessibleText="%main.vaultDetail.revealBtn">
<graphic>
<HBox spacing="12" alignment="CENTER">
<FontAwesome5IconView glyph="HDD" glyphSize="24"/>
@@ -25,8 +25,11 @@
</VBox>
</HBox>
</graphic>
<tooltip>
<Tooltip text="%main.vaultDetail.revealBtn"/>
</tooltip>
</Button>
<Button styleClass="button-large" contentDisplay="GRAPHIC_ONLY" minWidth="120" onAction="#copyMountUri" defaultButton="${controller.accessibleViaUri}" visible="${controller.accessibleViaUri}" managed="${controller.accessibleViaUri}">
<Button styleClass="button-large" contentDisplay="GRAPHIC_ONLY" minWidth="120" onAction="#copyMountUri" defaultButton="${controller.accessibleViaUri}" visible="${controller.accessibleViaUri}" managed="${controller.accessibleViaUri}" accessibleText="%main.vaultDetail.copyUri">
<graphic>
<HBox spacing="12" alignment="CENTER">
<FontAwesome5IconView glyph="LINK" glyphSize="24"/>
@@ -36,6 +39,9 @@
</VBox>
</HBox>
</graphic>
<tooltip>
<Tooltip text="%main.vaultDetail.copyUri"/>
</tooltip>
</Button>
<Button text="%main.vaultDetail.lockBtn" minWidth="120" onAction="#lock">
<graphic>

View File

@@ -38,14 +38,17 @@
</VBox>
</StackPane>
<HBox styleClass="button-bar">
<Button fx:id="addVaultButton" onMouseClicked="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY">
<Button fx:id="addVaultButton" onAction="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY" accessibleText="%main.vaultlist.addVaultButton.tooltip">
<graphic>
<FontAwesome5IconView glyph="PLUS" glyphSize="16"/>
</graphic>
<tooltip>
<Tooltip text="%main.vaultlist.addVaultButton.tooltip"/>
</tooltip>
</Button>
<Region HBox.hgrow="ALWAYS"/>
<StackPane>
<Button onMouseClicked="#showEventViewer" styleClass="button-right" minWidth="20" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false">
<Button onAction="#showEventViewer" styleClass="button-right" minWidth="20" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" accessibleText="%main.vaultlist.showEventsButton.tooltip">
<graphic>
<FontAwesome5IconView glyph="BELL" glyphSize="16"/>
</graphic>
@@ -57,10 +60,13 @@
<Circle radius="4" styleClass="icon-update-indicator" AnchorPane.topAnchor="-8" AnchorPane.rightAnchor="-6" visible="${controller.unreadEventsPresent}" />
</AnchorPane>
</StackPane>
<Button onMouseClicked="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY">
<Button onAction="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY" accessibleText="%main.vaultlist.showPreferencesButton.tooltip">
<graphic>
<FontAwesome5IconView glyph="COG" glyphSize="16"/>
</graphic>
<tooltip>
<Tooltip text="%main.vaultlist.showPreferencesButton.tooltip"/>
</tooltip>
</Button>
</HBox>
</VBox>

View File

@@ -22,14 +22,14 @@
</padding>
<children>
<HBox spacing="6" alignment="CENTER_LEFT">
<Label text="%vaultOptions.general.vaultName"/>
<Label text="%vaultOptions.general.vaultName" labelFor="$vaultName"/>
<TextField fx:id="vaultName"/>
</HBox>
<TextFlow styleClass="text-flow" prefWidth="-Infinity">
<CheckBox text="%vaultOptions.general.autoLock.lockAfterTimePart1" fx:id="lockAfterTimeCheckbox"/>
<Text text=" "/>
<NumericTextField fx:id="lockTimeInMinutesTextField" prefWidth="50"/>
<NumericTextField fx:id="lockTimeInMinutesTextField" prefWidth="50" accessibleText="%vaultOptions.general.autoLock.accessibleText"/>
<Text text=" "/>
<FormattedLabel format="%vaultOptions.general.autoLock.lockAfterTimePart2"/>
</TextFlow>
@@ -37,7 +37,7 @@
<CheckBox text="%vaultOptions.general.unlockAfterStartup" fx:id="unlockOnStartupCheckbox"/>
<HBox spacing="6" alignment="CENTER_LEFT">
<Label text="%vaultOptions.general.actionAfterUnlock"/>
<Label text="%vaultOptions.general.actionAfterUnlock" labelFor="$actionAfterUnlockChoiceBox"/>
<ChoiceBox fx:id="actionAfterUnlockChoiceBox"/>
</HBox>

View File

@@ -26,9 +26,9 @@
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%vaultOptions.mount.volume.type"/>
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
<graphic>
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
</graphic>
@@ -36,7 +36,7 @@
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
</tooltip>
</Hyperlink>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}">
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}" accessibleText="%preferences.volume.docsTooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
@@ -49,7 +49,7 @@
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
<Label text="%vaultOptions.mount.volume.tcp.port"/>
<Label text="%vaultOptions.mount.volume.tcp.port" labelFor="$vaultLoopbackPortField"/>
<NumericTextField fx:id="vaultLoopbackPortField"/>
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
</HBox>
@@ -58,7 +58,7 @@
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity">
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity" accessibleText="%vaultOptions.mount.customMountFlags">
<VBox.margin>
<Insets left="24"/>
</VBox.margin>
@@ -75,7 +75,7 @@
<HBox spacing="6" visible="${controller.mountpointDriveLetterSupported}" managed="${controller.mountpointDriveLetterSupported}">
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDriveLetterBtn" text="%vaultOptions.mount.mountPoint.driveLetter"/>
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}"/>
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}" accessibleText="%vaultOptions.mount.mountPoint.driveLetter"/>
</HBox>
<VBox spacing="6" visible="${controller.mountpointDirSupported}" managed="${controller.mountpointDirSupported}">
@@ -87,7 +87,7 @@
</graphic>
</Button>
</HBox>
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false">
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false" accessibleText="%vaultOptions.mount.mountPoint.custom">
<VBox.margin>
<Insets left="24"/>
</VBox.margin>

View File

@@ -259,6 +259,8 @@ health.check.detail.checkFinishedAndFound=The check finished running. Please rev
health.check.detail.checkFailed=The check exited due to an error.
health.check.detail.checkCancelled=The check was cancelled.
health.check.detail.listFilters.label=Filter
health.check.detail.filterSeverity=Filter by severity
health.check.detail.filterFixState=Filter by fix state
health.check.detail.fixAllSpecificBtn=Fix all of type
health.check.exportBtn=Export Report
## Result view
@@ -348,6 +350,7 @@ preferences.contribute.promptText=Paste supporter certificate code here
preferences.contribute.thankYou=Thank you for supporting Cryptomator's open-source development!
preferences.contribute.donate=Donate
preferences.contribute.sponsor=Sponsor
preferences.contribute.removeCert.tooltip=Remove certificate
### Remove License Key Dialog
removeCert.title=Remove Certificate
@@ -357,6 +360,7 @@ removeCert.description=Cryptomator's core features are not affected by this. Nei
## About
preferences.about=About
preferences.about.thirdPartyLicenses=Third-party licenses
# Vault Statistics
stats.title=Statistics for %s
@@ -406,7 +410,9 @@ main.vaultlist.contextMenu.share=Share…
main.vaultlist.addVaultBtn.menuItemNew=Create New Vault…
main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault…
main.vaultlist.addVaultBtn.menuItemRecover=Recover Existing Vault…
main.vaultlist.showEventsButton.tooltip=Open event view
main.vaultlist.addVaultButton.tooltip=Add Vault
main.vaultlist.showEventsButton.tooltip=Open Event View
main.vaultlist.showPreferencesButton.tooltip=Show Preferences
##Notificaition
main.notification.updateAvailable=Update is available.
main.notification.support=Support Cryptomator.
@@ -470,6 +476,7 @@ vaultOptions.general=General
vaultOptions.general.vaultName=Vault Name
vaultOptions.general.autoLock.lockAfterTimePart1=Lock when idle for
vaultOptions.general.autoLock.lockAfterTimePart2=minutes
vaultOptions.general.autoLock.accessibleText=Lock timeout in minutes
vaultOptions.general.unlockAfterStartup=Unlock vault when starting Cryptomator
vaultOptions.general.actionAfterUnlock=After successful unlock
vaultOptions.general.actionAfterUnlock.ignore=Do nothing
@@ -653,6 +660,8 @@ decryptNames.filePicker.title=Select encrypted file
decryptNames.filePicker.extensionDescription=Cryptomator encrypted file
decryptNames.copyTable.tooltip=Copy table
decryptNames.clearTable.tooltip=Clear table
decryptNames.column.encrypted=Encrypted
decryptNames.column.decrypted=Decrypted
decryptNames.copyHint=Copy cell content with %s
decryptNames.dropZone.message=Drop files or click to select
decryptNames.dropZone.error.vaultInternalFiles=Vault internal files with no decrypt-able name selected
@@ -665,6 +674,8 @@ decryptNames.dropZone.error.generic=Failed to decrypt file names
eventView.title=Events
eventView.filter.allVaults=All
eventView.clearListButton.tooltip=Clear list
eventView.filterVaults=Filter by vault
eventView.cell.actionsButton.tooltip=Event actions
## event list entries
eventView.entry.vaultLocked.description=Unlock "%s" for details
eventView.entry.conflictResolved.message=Resolved conflict